LinenFlow API
Read-only REST access to your linen data. Version 1.
Overview
The LinenFlow API is a read-only REST interface to your linen data — deliveries, manifests, inventory, invoices, orders, and quality. Point any tool that speaks HTTPS at it: Excel, a BI dashboard, a script. A key sees exactly the customers granted to it, and nothing else exists to it.
Base URL: https://api.linenflow.co/v1. Every response is JSON. The machine-readable OpenAPI spec mirrors this page.
Five-minute quickstart
- Create a key: in the customer portal, open Account → API → New Key and copy it — the key is shown once.
- Confirm it works:bash
curl -s https://api.linenflow.co/v1/customers \ -H "Authorization: Bearer YOUR_KEY" - Pull the last seven days of deliveries with par and fill rate:bash
curl -s https://api.linenflow.co/v1/deliveries/daily \ -H "Authorization: Bearer YOUR_KEY" - Connect Excel — see the Power Query recipe. Paste your key, refresh daily.
Authentication
Every request carries your key over HTTPS:
Authorization: Bearer lfk_live_…- Keys are created in the portal (Account → API) or by your provider.
- In the examples below, replace
YOUR_KEYwith your whole key — it already starts withlfk_live_, so don’t add that prefix again. - A key sees exactly the customers granted to it.
- Scopes gate endpoint groups:
processing,accounting,direct_sales. Calling an endpoint your key’s scope doesn’t cover returns403. - Revoking a key stops it on the next request.
- Never put a key in a URL, a spreadsheet cell, or shared code.
Pagination & filtering
Every list response uses one envelope:
{
"data": [ … ],
"pagination": { "cursor": "…", "has_more": true },
"request_id": "req_…"
}Pass ?cursor= from the previous page until has_more is false. limit is 1–200 (default 50). Every list also accepts customer_id (within your access) and, where a resource has a natural date, from/to (YYYY-MM-DD, inclusive, in your organization’s timezone). Unknown parameters are rejected with 422.
Field conventions
- Field names are snake_case; timestamps are ISO 8601 UTC.
- Money is integer cents with a
currencyfield. The one exception is direct-sales charge unit prices, which are mills (unit_price_mills, 1/1000 dollar); extended amounts stay cents. - Weights are pounds (
*_lb), to one decimal. - Ids are stable strings; document numbers are for display, not lookups.
Errors
Errors use one shape and honest status codes:
{ "error": { "code": "not_found", "message": "…" }, "request_id": "req_…" }401 unauthorized Missing, unknown, or revoked key
403 forbidden The key lacks the endpoint's scope
404 not_found Unknown route, or a resource outside your access
405 method_not_allowed The API is read-only — use GET
422 invalid_params A parameter is missing, unknown, or malformed
429 rate_limited Slow down — honor the Retry-After header
500 internal_error Unexpected failure — quote request_id to supportRate limits
120 requests per minute per key. On 429, wait the Retry-After seconds and continue. Every request is logged against your key; you can see a key’s last use and volume where you manage it.
Recipe — Excel (refreshable table)
The daily-deliveries feed lands in Excel as a table you refresh with one click — no copying numbers by hand.
- Data → Get Data → From Other Sources → Blank Query.
- Home → Advanced Editor, paste the code below, set your key on line 2.
- Done. If asked how to connect, choose Anonymous — the header authenticates.
- Close & Load. Then Data → Refresh All re-pulls any time.
Power Query (M)
let
ApiKey = "YOUR_KEY",
BaseUrl = "https://api.linenflow.co/v1/deliveries/daily?limit=200",
GetPage = (cursor as nullable text) =>
Json.Document(
Web.Contents(
BaseUrl & (if cursor = null then "" else "&cursor=" & Uri.EscapeDataString(cursor)),
[Headers = [Authorization = "Bearer " & ApiKey]]
)
),
Pages = List.Generate(
() => GetPage(null),
each _ <> null,
each if _[pagination][has_more] then GetPage(_[pagination][cursor]) else null
),
Rows = List.Combine(List.Transform(Pages, each _[data])),
Table = Table.FromRecords(Rows,
{"date", "customer_name", "department_name", "item_name",
"delivered_count", "daily_par", "fill_rate"}, MissingField.UseNull)
in
TableWant a fixed window? Add &from=2026-08-01&to=2026-08-11 to BaseUrl. The same pattern works for every list endpoint — swap the URL and the column list.
Recipe — Python
import requests
BASE = "https://api.linenflow.co/v1"
session = requests.Session()
session.headers["Authorization"] = "Bearer YOUR_KEY"
def fetch_all(path, **params):
rows, cursor = [], None
while True:
query = {**params, "limit": 200}
if cursor:
query["cursor"] = cursor
resp = session.get(f"{BASE}{path}", params=query, timeout=30)
resp.raise_for_status()
body = resp.json()
rows.extend(body["data"])
page = body.get("pagination") or {}
if not page.get("has_more"):
return rows
cursor = page["cursor"]
deliveries = fetch_all("/deliveries/daily", **{"from": "2026-08-01", "to": "2026-08-11"})
print(len(deliveries), "rows")Recipe — Google Sheets
Extensions → Apps Script, paste the function, add a daily time-driven trigger. It fills a tab named “Deliveries”.
function pullDeliveries() {
const KEY = 'YOUR_KEY';
let rows = [], cursor = null;
do {
let url = 'https://api.linenflow.co/v1/deliveries/daily?limit=200'
+ (cursor ? '&cursor=' + encodeURIComponent(cursor) : '');
const body = JSON.parse(UrlFetchApp.fetch(url, {
headers: { Authorization: 'Bearer ' + KEY },
}).getContentText());
rows = rows.concat(body.data);
cursor = body.pagination.has_more ? body.pagination.cursor : null;
} while (cursor);
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName('Deliveries') || ss.insertSheet('Deliveries');
sheet.clearContents();
sheet.appendRow(['date', 'customer', 'department', 'item', 'delivered', 'par', 'fill_rate']);
rows.forEach(r => sheet.appendRow([r.date, r.customer_name, r.department_name,
r.item_name, r.delivered_count, r.daily_par, r.fill_rate]));
}Planned
v1 is read-only by design. Planned for later: POST /quality-tickets, POST /staged-pickups, and webhooks for delivery and invoice events. Manage keys anytime in Settings → API or the portal’s Account → API.
Resources
Each resource is a table you can query. Lists paginate by cursor and accept customer_id plus the filters noted. Fields below are the exact response columns; sample rows use fictional data.
Customers
any key/api/v1/customers— List the customers this key can accessThe customers this key can access, each with a summary of its departments. Use these ids as the customer_id filter on every other endpoint.
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Customer id (cuid), reused as-is. Pass as the customer_id filter on other endpoints. |
| name | string | Customer display name. |
| is_group | boolean | True when the customer is a parent group that rolls up child locations; false for a single billed location. |
| created_at | timestamp | ISO 8601 UTC. When the customer record was created. |
| departments | array | Array of {id, name} objects, sorted by name. Full department detail (including cost_center_code) is at /customers/{id}/departments. |
Sample data
| id | name | is_group | created_at |
|---|---|---|---|
| cus_grandview | Grandview Medical Center | false | 2026-01-05T14:00:00.000Z |
| cus_riverbend | Riverbend Surgical Center | false | 2026-02-18T16:30:00.000Z |
| cus_northwind | Northwind Hotel | true | 2026-03-02T11:15:00.000Z |
Example request
curl -s "https://api.linenflow.co/v1/customers?limit=50" \
-H "Authorization: Bearer YOUR_KEY"Customer Departments
any key/api/v1/customers/{id}/departments— List one customer's departmentsThe departments (cost centers) under one customer. An out-of-scope or unknown customer id returns 404, never 403.
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Department id (cuid). |
| customer_id | string | Parent customer id — echoes the {id} in the path. |
| name | string | Department display name (e.g. Surgery, ICU). |
| cost_center_code | string | null | Customer's accounting cost-center code for the department; null when unset. |
Sample data
| id | customer_id | name | cost_center_code |
|---|---|---|---|
| dep_surgery | cus_grandview | Surgery | 4400 |
| dep_icu | cus_grandview | ICU | 4410 |
| dep_housekeeping | cus_grandview | Housekeeping | — |
Example request
curl -s "https://api.linenflow.co/v1/customers/cus_grandview/departments" \
-H "Authorization: Bearer YOUR_KEY"Items
any key/api/v1/items— List catalog items relevant to the key's customersThe catalog items relevant to the key's customers — the active customer and department catalog union. Filtered to a single customer_id, weight_lb reflects that customer's per-item weight override when set.
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Item id (cuid). |
| sku | string | Catalog stock-keeping unit / code. |
| name | string | Item display name. |
| category | string | null | Catalog category name; null when the item is uncategorized. |
| unit_of_measure | string | Counting/billing unit, e.g. 'each'. |
| weight_lb | decimal | null | Per-piece weight in POUNDS; null when unset. Filtered to a single customer_id, this is that customer's weight override when set, otherwise the catalog default. |
Sample data
| id | sku | name | category | unit_of_measure | weight_lb |
|---|---|---|---|---|---|
| itm_bath22 | BTH-2244-W | Bath Towel 22x44 White | Towels | each | 0.65 |
| itm_pillowking | PLW-KING-W | Pillowcase King White | Linens | each | 0.3 |
| itm_gownlg | GWN-SRG-L | Surgical Gown Large | Surgical | each | 1.1 |
Example request
curl -s "https://api.linenflow.co/v1/items?customer_id=cus_grandview&limit=100" \
-H "Authorization: Bearer YOUR_KEY"Manifests
processing/api/v1/manifests— List manifests/api/v1/manifests/{id}— Get one manifest with bins, rewash links, and signature metadataThe operational heartbeat: every manifest through the Collected → Received → Cleaned → Staged → Delivered lifecycle, with soil and clean weights, piece counts, and wash-loss variance. The detail endpoint adds bins, rewash links, and delivery-signature metadata.
Date filter: from / to on delivered_at.
Filters:department_idtype — normal | rewash | new_linenstatus — collected | received | cleaned | staged | deliveredfrom / to — on the delivered day; undelivered manifests are excluded when a window is set
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Manifest cuid. |
| manifest_number | string | Display number (e.g. MAN-26-088213). Never a lookup key — unique only per plant. |
| type | string | normal | rewash | new_linen. |
| status | string | collected | received | cleaned | staged | delivered. |
| customer_id | string | Owning customer cuid. |
| department_id | string | null | null for customer-wide manifests. |
| department_name | string | null | null when there is no department. |
| created_at | timestamp | ISO 8601 UTC. Manifest created (collected). |
| picked_up_at | timestamp | null | ISO 8601 UTC, or null. |
| received_at | timestamp | null | ISO 8601 UTC, or null. |
| cleaned_at | timestamp | null | ISO 8601 UTC, or null. |
| staged_at | timestamp | null | ISO 8601 UTC, or null. |
| delivered_at | timestamp | null | ISO 8601 UTC, or null. Primary date; from/to filter on this. |
| soil_weight_lb | decimal | null | Pounds, rounded to 0.1. Soil-side net weight. |
| clean_weight_lb | decimal | null | Pounds, rounded to 0.1. Clean-side net weight. |
| clean_piece_count | int | null | Counted clean pieces, or null. |
| variance_pct | decimal | null | (soil − clean) ÷ soil; positive = normal wash loss, negative when clean > soil. |
| variance_within_tolerance | boolean | null | Whether variance is inside the org tolerance. |
| parent_manifest_id | string | null | Set on rewash manifests (mother/child link). |
| amount_cents | int | null | Integer cents; snapshot of the billed amount. |
| currency | string | Always 'usd'. |
| bins | array | Detail only (manifests/{id}). Receiving (soil) + shipping (clean) bins; each has side, content, gross_lb, tare_lb, net_lb, and item lines. |
| held_rewash | object | null | Detail only. Held-back rewash summary, or null. soil_rewash_lb / error_rewash_lb are null when the org hides the fault split. |
| rewash_links | object | Detail only. { parent_manifest: {id, manifest_number} | null, rewash_children: [{id, manifest_number}] }. |
| signature | object | Detail only. Delivery signature metadata: { signed_by_name, signed_by_title, delivered_at }. Never geo or image keys. |
Sample data
| manifest_number | type | status | delivered_at | soil_weight_lb | clean_weight_lb | variance_pct |
|---|---|---|---|---|---|---|
| MAN-26-088213 | normal | delivered | 2026-08-11T14:22:00.000Z | 118.4 | 108.2 | 0.086 |
| MAN-26-088214 | normal | delivered | 2026-08-11T15:48:00.000Z | 96.3 | 90.1 | 0.064 |
| MAN-26-088180 | rewash | delivered | 2026-08-10T13:10:00.000Z | 22.5 | 21.8 | 0.031 |
Example request
curl "https://api.linenflow.co/v1/manifests?status=delivered&type=normal&limit=50" \
-H "Authorization: Bearer YOUR_KEY"Daily Deliveries
processing/api/v1/deliveries/daily— Daily delivered counts by customer, department, and item with par and fill ratePer day × item × department delivered counts with par and fill rate — the daily-counts feed that replaces manual spreadsheet entry.
Date filter: from / to on delivery day (org timezone).
Filters:from / to — delivery day, org timezone (defaults to the last 7 days, max 92)department_iditem_id
Columns
| Field | Type | Description |
|---|---|---|
| date | string | Delivery day (org timezone), YYYY-MM-DD. |
| customer_id | string | Customer cuid. |
| customer_name | string | Customer display name. |
| department_id | string | null | null for customer-level rows. |
| department_name | string | null | null for customer-level rows. |
| item_id | string | Item cuid. |
| item_name | string | Item display name. |
| delivered_count | int | Sum of cleaned pieces delivered that day; held rewash and damage pieces are excluded. |
| daily_par | int | null | Live pieces par (department row, falling back to customer-wide); null when there is no par or the par is in cart_loads. |
| fill_rate | decimal | null | delivered_count ÷ daily_par, 3 decimals; null without a usable pieces par. |
Sample data
| date | customer_name | department_name | item_name | delivered_count | daily_par | fill_rate |
|---|---|---|---|---|---|---|
| 2026-08-11 | Grandview Medical Center | Surgery | Bath Towel 22x44 White | 300 | 360 | 0.833 |
| 2026-08-11 | Riverbend Surgical Center | ICU | Pillowcase King White | 240 | 220 | 1.091 |
| 2026-08-10 | Northwind Hotel | Housekeeping | Bar Mop White | 480 | — | — |
Example request
curl "https://api.linenflow.co/v1/deliveries/daily?from=2026-08-05&to=2026-08-11&department_id=dep_surgery" \
-H "Authorization: Bearer YOUR_KEY"Inventory Totals
processing/api/v1/inventory/totals— List per-item inventory totals by locationPer-item inventory rollup across every location: total owned plus at-customer, at-facility, in-transit, and retired counts.
Columns
| Field | Type | Description |
|---|---|---|
| item_id | string | Item cuid. |
| item_name | string | Item display name. |
| total_owned | int | at_customer + at_facility + in_transit (retired units excluded). |
| at_customer | int | Units at the customer or in service. |
| at_facility | int | Units new, in reserve, or in plant. |
| in_transit | int | Units in transit. |
| retired | int | Units in any retired state. |
Sample data
| item_id | item_name | total_owned | at_customer | at_facility | in_transit | retired |
|---|---|---|---|---|---|---|
| itm_bath22 | Bath Towel 22x44 White | 4200 | 2600 | 1400 | 200 | 340 |
| itm_pillowking | Pillowcase King White | 3100 | 1900 | 1050 | 150 | 210 |
| itm_gownlg | Surgical Gown Large | 900 | 540 | 300 | 60 | 75 |
Example request
curl "https://api.linenflow.co/v1/inventory/totals?customer_id=cus_grandview" \
-H "Authorization: Bearer YOUR_KEY"Par Levels
processing/api/v1/inventory/par— List live par levelsLive par levels per item × department — the target holdings behind the fill-rate denominator. A null department is the customer-wide row.
Filters:department_id
Columns
| Field | Type | Description |
|---|---|---|
| customer_id | string | Customer cuid. |
| department_id | string | null | null = the customer-wide par row. |
| department_name | string | null | null for customer-wide rows. |
| item_id | string | Item cuid. |
| item_name | string | Item display name. |
| par_quantity | int | Target quantity in par_unit. |
| par_unit | string | pieces | cart_loads. |
| reorder_threshold | int | Reorder trigger level; 0 when unset. |
Sample data
| item_name | department_name | par_quantity | par_unit | reorder_threshold |
|---|---|---|---|---|
| Bath Towel 22x44 White | Surgery | 360 | pieces | 120 |
| Pillowcase King White | ICU | 220 | pieces | 80 |
| Bar Mop White | — | 30 | cart_loads | 8 |
Example request
curl "https://api.linenflow.co/v1/inventory/par?customer_id=cus_grandview&department_id=dep_surgery" \
-H "Authorization: Bearer YOUR_KEY"Units
processing/api/v1/inventory/units— List unitsThe floors and areas that hold carts. A key whose user counts only some units reads only those units.
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Unit cuid. |
| customer_id | string | Customer cuid. |
| name | string | Unit name. |
| building | string | null | Building, when recorded. |
| floor | string | null | Floor, when recorded. |
| unit_type | string | null | Free-form type, e.g. med_surg. |
| cost_center | string | null | The customer’s cost center code. |
| cart_count | int | Carts in the unit. |
| is_active | boolean | false = retired unit. |
Sample data
| name | building | floor | cost_center | cart_count | is_active |
|---|---|---|---|---|---|
| 8 North East | Main | 8 | 70110 | 3 | true |
| Surgery PACU | Main | 2 | 70450 | 2 | true |
Example request
curl "https://api.linenflow.co/v1/inventory/units?customer_id=cus_grandview" \
-H "Authorization: Bearer YOUR_KEY"Carts
processing/api/v1/inventory/carts— List carts with their parEvery cart or closet of a unit with its live par sheet and the days it is counted.
Filters:unit_id
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Cart cuid. |
| customer_id | string | Customer cuid. |
| unit_id | string | Unit cuid. |
| unit_name | string | null | Unit name. |
| name | string | Cart name. |
| kind | string | cart | closet | linen_room | exchange_cart. |
| stocking_method | string | fixed | census. |
| count_days | array | sun … sat — the days the cart is counted. |
| is_active | boolean | false = retired cart. |
| par | array | item_id · item_name · par_quantity, live rows only. |
Sample response
{
"id": "cart_8ne1",
"customer_id": "cus_grandview",
"unit_id": "unit_8ne",
"unit_name": "8 North East",
"name": "8 North East 1",
"kind": "cart",
"stocking_method": "fixed",
"count_days": [
"mon",
"tue",
"wed",
"thu",
"fri"
],
"is_active": true,
"par": [
{
"item_id": "itm_bathtowel",
"item_name": "Bath Towel 22x44 White",
"par_quantity": 40
},
{
"item_id": "itm_washcloth",
"item_name": "Washcloth White",
"par_quantity": 60
}
]
}Example request
curl "https://api.linenflow.co/v1/inventory/carts?customer_id=cus_grandview&unit_id=unit_8ne" \
-H "Authorization: Bearer YOUR_KEY"Cart Counts
processing/api/v1/inventory/counts— List cart countsThe daily cart counts with one line per item: the par when the count was generated, what was on hand, what was required and what was put on the cart.
Date filter: from / to on count day.
Filters:from / to — on the org-local count day (default the last 7 days, 92 days at most)unit_idcart_idstatus — pending | in_progress | counted | verified | assumed
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Count cuid. |
| customer_id | string | Customer cuid. |
| cart_id | string | Cart cuid. |
| cart_name | string | null | Cart name. |
| unit_id | string | null | Unit cuid. |
| unit_name | string | null | Unit name. |
| date | date | Org-local count day. |
| status | string | pending | in_progress | counted | verified | assumed. |
| unscheduled | boolean | true = counted on a day the cart is not due. |
| counted_at | timestamp | null | When the count was marked done. |
| verified_at | timestamp | null | When the laundry verified it. |
| lines | array | item_id · item_name · par · on_hand · required · delivered · extra · returned. |
Sample response
{
"id": "cnt_0921_8ne1",
"customer_id": "cus_grandview",
"cart_id": "cart_8ne1",
"cart_name": "8 North East 1",
"unit_id": "unit_8ne",
"unit_name": "8 North East",
"date": "2026-09-21",
"status": "counted",
"unscheduled": false,
"counted_at": "2026-09-21T10:42:00.000Z",
"verified_at": null,
"lines": [
{
"item_id": "itm_bathtowel",
"item_name": "Bath Towel 22x44 White",
"par": 40,
"on_hand": 12,
"required": 28,
"delivered": 28,
"extra": 0,
"returned": 0
}
]
}Example request
curl "https://api.linenflow.co/v1/inventory/counts?customer_id=cus_grandview&from=2026-09-15&to=2026-09-21" \
-H "Authorization: Bearer YOUR_KEY"Balances
processing/api/v1/inventory/balances— List balances by item and statusStock by item and status, derived from the movement ledger. One row per item and status with a non-zero quantity.
Filters:item_id
Columns
| Field | Type | Description |
|---|---|---|
| item_id | string | Item cuid. |
| item_name | string | Item display name. |
| status | string | new | in_reserve | in_plant | in_transit | at_customer | retired_*. |
| quantity | int | Pieces in that status. |
Sample data
| item_name | status | quantity |
|---|---|---|
| Bath Towel 22x44 White | in_reserve | 350 |
| Bath Towel 22x44 White | at_customer | 800 |
| Bath Towel 22x44 White | retired_lost | 30 |
Example request
curl "https://api.linenflow.co/v1/inventory/balances?customer_id=cus_grandview" \
-H "Authorization: Bearer YOUR_KEY"Inventory Signals
processing/api/v1/inventory/signals— List inventory signalsThe nightly advisory signals: short fills, eroding fill, injection dependency, over fill, dormant stock, circulation breaches. Open and acknowledged by default.
Filters:kindstatus — open | acknowledged | dismissed | resolved
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Signal cuid. |
| kind | string | shortfall_streak | rolling_erosion | injection_dependency | over_fill | dormant_stock | circulation_breach | unmatched_injection | … |
| severity | string | low | medium | high. |
| status | string | open | acknowledged | dismissed | resolved. |
| customer_id | string | null | Customer cuid. |
| department_id | string | null | Department cuid, when the signal is per department. |
| item_id | string | null | Item cuid; null = all items. |
| item_name | string | null | Item display name. |
| summary | string | One line describing the condition. |
| suggested_order_qty | int | null | Circulation breaches only; rounded to the pack size. |
| window_start | date | First day of the window the signal read. |
| window_end | date | Last day of the window. |
| generated_at | timestamp | The last nightly run that confirmed it. |
Sample data
| kind | severity | item_name | summary | status |
|---|---|---|---|---|
| shortfall_streak | high | Bath Towel 22x44 White | 7 service days under 85% · average 70% | open |
| dormant_stock | low | Bar Mop White | No delivery in 60 days against a par of 30 | acknowledged |
Example request
curl "https://api.linenflow.co/v1/inventory/signals?customer_id=cus_grandview&kind=shortfall_streak" \
-H "Authorization: Bearer YOUR_KEY"New Linen
processing/api/v1/new-linen— List new-linen injectionsNew-linen injection manifests: the clean linen injected into a customer's stock, with per-item counts and injected weight. Direction is always inbound.
Date filter: from / to on cleaned day.
Filters:from / to — on the org-local cleaned day
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Manifest cuid. |
| manifest_number | string | Display number (e.g. MAN-26-088260). |
| date | string | null | Org-local cleaned day (YYYY-MM-DD); null when not yet cleaned. |
| customer_id | string | Customer cuid. |
| department_id | string | null | null for customer-wide injections. |
| direction | string | Always 'in' (injections; the model has no outbound). |
| soil_weight_lb | decimal | null | Pounds, rounded to 0.1. Soil weigh-in. |
| injected_weight_lb | decimal | null | Pounds, rounded to 0.1. Clean injected weight. |
| items | array | Per-item injected counts: [{ item_id, item_name, piece_count }]. |
Sample data
| manifest_number | date | direction | soil_weight_lb | injected_weight_lb |
|---|---|---|---|---|
| MAN-26-088260 | 2026-08-10 | in | 60.4 | 59.8 |
| MAN-26-088188 | 2026-08-08 | in | 42.1 | 41.7 |
| MAN-26-088090 | 2026-08-05 | in | 88 | 87.2 |
Example request
curl "https://api.linenflow.co/v1/new-linen?from=2026-08-01&to=2026-08-11" \
-H "Authorization: Bearer YOUR_KEY"Rewash
processing/api/v1/rewash— List held and resolved rewash batchesHeld and resolved rewash batches: held weight, fault split, the mother manifest, and the manifest that later consumed each batch.
Date filter: from / to on discovered date (created_at).
Filters:status — pending | consumed | voidedfrom / to — on the discovered (created) date
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Pending-rewash cuid. |
| customer_id | string | Customer cuid. |
| department_id | string | null | null for customer-wide batches. |
| discovered_at | timestamp | ISO 8601 UTC (created date). Primary date; from/to filter on this. |
| status | string | pending | consumed | voided. |
| held_weight_lb | decimal | null | Pounds, rounded to 0.1. Override-aware total held weight. |
| soil_rewash_lb | decimal | null | Pounds. null when the org hides the fault split from customers. |
| error_rewash_lb | decimal | null | Pounds. null when the org hides the fault split from customers. |
| parent_manifest | object | { id, manifest_number } of the mother manifest. |
| consumed_by_manifest | object | null | { id, manifest_number, delivered_at } once consumed, else null. |
| outcomes | array | Fault outcomes: [{ fault_class, good_lb, damage_lb }]. |
Sample data
| id | status | discovered_at | held_weight_lb | soil_rewash_lb | error_rewash_lb |
|---|---|---|---|---|---|
| rw_51204 | consumed | 2026-08-09T14:20:00.000Z | 62.4 | 40.1 | 22.3 |
| rw_51330 | pending | 2026-08-11T08:12:00.000Z | 15.6 | 9.4 | 6.2 |
| rw_51188 | voided | 2026-08-07T16:40:00.000Z | 8 | 5 | 3 |
Example request
curl "https://api.linenflow.co/v1/rewash?status=consumed&from=2026-08-01&to=2026-08-11" \
-H "Authorization: Bearer YOUR_KEY"Invoices
accounting/api/v1/invoices— List sent invoices with totals, balance, and a signed PDF linkSent, partially paid, and paid invoices with their totals, surcharges, tax, and outstanding balance in integer cents. Drafts, voided, and written-off invoices never appear on the public API. Each row carries pdf_url — a short-lived (~15 minute) signed link you fetch directly with your key to download the invoice PDF.
Date filter: from / to on issued date.
Filters:status — sent | partially_paid | paidfrom / to — on the issued date (org timezone)
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Invoice id (cuid). Stable lookup key. |
| invoice_number | string | Display document number (PREFIX-date-sequence, e.g. INV-26-00218). Unique per org but a display field — never a lookup key. |
| status | string | One of sent, partially_paid, paid. These are the only statuses that ever appear publicly; drafts, void, and written-off are never returned. |
| customer_id | string | Owning customer id (cuid). |
| department_id | string | null | Set when the invoice is scoped to a single department; null for a consolidated invoice spanning departments. |
| issued_at | timestamp | ISO 8601 UTC. The issued date (this list’s from/to primary date). |
| due_at | timestamp | null | ISO 8601 UTC payment due date; null when the customer has no payment terms set. |
| sent_at | timestamp | ISO 8601 UTC. When the invoice was sent — always present, since only sent/partially_paid/paid invoices appear. |
| paid_at | timestamp | null | ISO 8601 UTC. When the invoice was paid in full; null until then. |
| subtotal_cents | int | Integer cents, before surcharges and tax. |
| surcharges_cents | int | Integer cents of surcharges. |
| tax_cents | int | Integer cents of tax. |
| total_cents | int | Integer cents. The invoice total (subtotal + surcharges + tax). |
| balance_cents | int | Integer cents still outstanding; 0 once paid. |
| currency | string | ISO 4217 currency, lowercase (e.g. usd). |
| pdf_url | string | Signed link to the invoice PDF that expires in about 15 minutes. Fetch it directly with your key (see the Invoice PDF resource); do not construct it yourself. |
Sample data
| invoice_number | status | issued_at | due_at | total_cents | balance_cents |
|---|---|---|---|---|---|
| INV-26-00218 | sent | 2026-08-01T14:30:00.000Z | 2026-08-31T14:30:00.000Z | 127500 | 127500 |
| INV-26-00219 | partially_paid | 2026-07-15T15:00:00.000Z | 2026-08-14T15:00:00.000Z | 342000 | 162000 |
| INV-26-00220 | paid | 2026-07-01T13:45:00.000Z | 2026-07-31T13:45:00.000Z | 89900 | 0 |
Example request
curl -s "https://api.linenflow.co/v1/invoices?status=sent&from=2026-08-01&to=2026-08-31" \
-H "Authorization: Bearer YOUR_KEY"Payments
accounting/api/v1/payments— List payments with their invoice allocationsCustomer payments with the amount, method, settlement status, and how each was allocated across invoices. Processor details and internal notes are never exposed.
Date filter: from / to on received date.
Filters:from / to — on the received date (org timezone)
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Payment id (cuid). |
| customer_id | string | Paying customer id (cuid). |
| kind | string | Payment method: one of card, ach, check, cash, credit_memo, write_off. |
| status | string | One of settled, returned, failed. |
| amount_cents | int | Integer cents received. |
| currency | string | ISO 4217 currency, lowercase (e.g. usd). |
| received_at | timestamp | ISO 8601 UTC. The received date (this list’s from/to primary date). |
| returned_at | timestamp | null | ISO 8601 UTC. Set when the payment was returned or reversed; null otherwise. |
| allocations | array | One object per invoice this payment was applied to, each { invoice_id: string | null, invoice_number: string | null, amount_cents: int }. invoice_id and invoice_number are null when that invoice falls outside your key’s customer scope; amount_cents still counts, so the allocations always reconcile to amount_cents. |
Sample data
| id | kind | status | amount_cents | received_at |
|---|---|---|---|---|
| pay_88012 | check | settled | 127500 | 2026-08-10T14:00:00.000Z |
| pay_88013 | ach | settled | 222000 | 2026-08-05T16:20:00.000Z |
| pay_88014 | card | returned | 50000 | 2026-07-28T13:10:00.000Z |
Example request
curl -s "https://api.linenflow.co/v1/payments?from=2026-08-01&to=2026-08-31&limit=50" \
-H "Authorization: Bearer YOUR_KEY"A/R Balance
accounting/api/v1/balance— A/R balance and aging buckets per customerOpen accounts-receivable per customer: the total outstanding balance plus aging buckets by days past due. One row per customer your key can see; customer_id narrows to a single customer. Balances sum only sent and partially_paid invoices.
Columns
| Field | Type | Description |
|---|---|---|
| customer_id | string | Customer id (cuid). |
| customer_name | string | The customer’s name. |
| balance_cents | int | Integer cents. Total open balance across the customer’s sent and partially_paid invoices. |
| currency | string | ISO 4217 currency, lowercase (e.g. usd). |
| aging | object | Integer-cents buckets { current, d1_30, d31_60, d61_plus }, splitting balance_cents by each invoice’s days past its due date (current = not yet overdue). The four buckets sum to balance_cents. |
Sample data
| customer_name | balance_cents |
|---|---|
| Grandview Medical Center | 127500 |
| Riverbend Surgical Center | 162000 |
Example request
curl -s "https://api.linenflow.co/v1/balance?customer_id=cus_grandview" \
-H "Authorization: Bearer YOUR_KEY"Purchase Orders
accounting/api/v1/purchase-orders— List purchase orders with total, spent, and remaining amountsCustomer purchase orders — the pre-authorized budget each carries, plus how much has been drawn down and what remains. Invoices deduct from the matching active PO as they are sent.
Date filter: from / to on started date.
Filters:status — pending | active | closed | cancelled
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Purchase order id (cuid). |
| customer_id | string | Owning customer id (cuid). |
| po_number | string | The customer’s PO number (display field). |
| status | string | One of pending, active, closed, cancelled. |
| total_cents | int | Integer cents. The PO’s authorized budget. |
| spent_cents | int | Integer cents drawn down so far (= total_cents − remaining_cents). |
| remaining_cents | int | Integer cents left on the PO. |
| currency | string | ISO 4217 currency, lowercase (e.g. usd). |
| started_at | timestamp | ISO 8601 UTC. The start date (this list’s from/to primary date). |
| expires_at | timestamp | ISO 8601 UTC. When the PO expires. |
Sample data
| po_number | status | total_cents | spent_cents | remaining_cents | expires_at |
|---|---|---|---|---|---|
| PO-2026-118 | active | 5000000 | 1275000 | 3725000 | 2027-06-30T04:00:00.000Z |
| PO-2026-092 | pending | 2500000 | 0 | 2500000 | 2027-03-31T04:00:00.000Z |
| PO-2025-441 | closed | 1200000 | 1200000 | 0 | 2026-06-30T04:00:00.000Z |
Example request
curl -s "https://api.linenflow.co/v1/purchase-orders?status=active" \
-H "Authorization: Bearer YOUR_KEY"Invoice PDF
accounting/api/v1/files/invoice/{id}— Fetch one invoice PDF via its signed linkDownload a single invoice as a PDF. This endpoint returns the PDF bytes, not a JSON envelope. Do not build the URL yourself — use the signed, expiring pdf_url returned on GET /invoices.
Columns
| Field | Type | Description |
|---|---|---|
| (binary) | application/pdf | The response body is the invoice PDF (Content-Type: application/pdf), not JSON. You reach this endpoint through the pdf_url on GET /invoices — a signed link that expires in about 15 minutes and re-verifies your key and accounting scope at fetch time. PDFs are rendered on demand, never stored. |
Sample response
"Returns application/pdf bytes, not JSON. Fetch the signed pdf_url from GET /invoices and save the response to a file."Example request
curl -L -o invoice.pdf \
-H "Authorization: Bearer YOUR_KEY" \
"https://api.linenflow.co/v1/files/invoice/inv_40218?kid=key_a1b2c3&exp=1786000000&sig=Xy7RedactedSignature"Orders
direct_sales/api/v1/orders— List direct-sales orders/api/v1/orders/{id}— Fetch one order with lines and chargesDirect-sales orders (uniforms and embroidery), one row per order with wearer and total summary. Fetch a single order by id to get its full line items and per-line charges.
Date filter: from / to on placed date.
Filters:stage — draft | submitted | in_production | shipped | delivered | cancelledfrom / to — on the placed day (org timezone)
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Stable cuid order id. Use for orders/{id}. |
| order_number | string | Display document number (DS-YY-#####). Display field, never a lookup key. |
| stage | string | One of draft, submitted, in_production, shipped, delivered, cancelled. |
| customer_id | string | Owning customer cuid. |
| placed_at | timestamp | null | ISO 8601 UTC. When the order was submitted; null on drafts. Primary date for from/to. |
| requested_delivery_date | timestamp | null | ISO 8601 UTC. Delivery the customer asked for; null if unset. |
| confirmed_delivery_date | timestamp | null | ISO 8601 UTC. Delivery LinenFlow committed to; null until confirmed. |
| delivered_at | timestamp | null | ISO 8601 UTC. Null until the order reaches the delivered stage. |
| subtotal_cents | int | Integer cents. Sum of the line subtotals, before order-level charges. |
| total_cents | int | Integer cents. Order total including order-level charges. |
| currency | string | Always 'usd'. |
| orderer_name | string | null | Person who placed the order; null if unrecorded. |
| wearer_name | string | null | Wearer first + last, trimmed; null when both are empty. |
| wearer_department | string | null | Free-text wearer department (order's own field, not a linked department id); null if unset. |
| delivery_location | string | null | Free-text delivery location; null if unset. |
| po_number | string | null | Customer PO reference; null if none. |
| line_count | int | Number of lines on the order. |
Sample data
| order_number | stage | placed_at | wearer_name | total_cents | line_count |
|---|---|---|---|---|---|
| DS-26-00042 | in_production | 2026-08-03T14:10:00.000Z | Elena Ruiz | 28800 | 2 |
| DS-26-00039 | delivered | 2026-07-28T11:05:00.000Z | Marcus Bell | 15400 | 1 |
| DS-26-00051 | submitted | 2026-08-10T16:30:00.000Z | Priya Nair | 9600 | 1 |
Example request
curl -s 'https://api.linenflow.co/v1/orders?stage=in_production&from=2026-07-01&to=2026-08-12&limit=50' \
-H 'Authorization: Bearer YOUR_KEY'Quality Tickets
any key/api/v1/quality-tickets— List quality ticketsQuality tickets (stains, shortages, damage, late delivery) with their lifecycle timestamps. The event thread carries customer-visible entries only; internal notes are never exposed and reading is a no-op (no "viewed" event is written).
Date filter: from / to on created date.
Filters:status — new | acknowledged | in_progress | resolved | verifiedfrom / to — on the created day (org timezone)
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Stable cuid ticket id. |
| ticket_number | string | Display document number (TKT-YY-#####). Display field, not a lookup key. |
| kind | string | Raw kind: stain, rip_damage, shortage, wrong_item, late_delivery, billing_dispute, other (legacy rows may carry complaint, missing_linen, damage_claim). |
| kind_label | string | Human label for kind, e.g. 'Stain', 'Rip / damage', 'Billing question'. |
| status | string | One of new, acknowledged, in_progress, resolved, verified. |
| status_label | string | Human label: 'Submitted', 'Acknowledged', 'In progress', 'Resolved' (verified also shows 'Resolved'). |
| subject | string | Short ticket subject line. |
| created_at | timestamp | ISO 8601 UTC. Primary date for from/to. |
| acknowledged_at | timestamp | null | ISO 8601 UTC. When staff first acknowledged; null until then. |
| resolved_at | timestamp | null | ISO 8601 UTC. When the ticket was resolved; null while open. |
| resolution_note | string | null | How it was resolved; null while open. |
| last_correspondence_at | timestamp | null | ISO 8601 UTC. Timestamp of the latest reply on the thread; null if none. |
| customer_visible_events | array | Customer-visible thread entries only (internal notes excluded), oldest first. Each: { event_type, actor_kind, body (string|null), created_at }. |
Sample data
| ticket_number | kind_label | status_label | subject | created_at |
|---|---|---|---|---|
| TKT-26-00018 | Stain | Acknowledged | Rust stains on ICU bath towels | 2026-08-10T13:20:00.000Z |
| TKT-26-00015 | Shortage | Resolved | Short 40 pillowcases on Housekeeping cart | 2026-08-07T09:05:00.000Z |
| TKT-26-00021 | Late delivery | In progress | Surgery delivery arrived two hours late | 2026-08-11T15:40:00.000Z |
Example request
curl -s 'https://api.linenflow.co/v1/quality-tickets?status=acknowledged&from=2026-08-01&to=2026-08-12' \
-H 'Authorization: Bearer YOUR_KEY'Ratings
any key/api/v1/ratings— List delivery ratingsPer-delivery customer ratings: on-time flag, a 1–5 quality score, and an optional note. The manifest number resolves through a scope-re-pinned join, so a rating never surfaces another customer’s manifest number.
Date filter: from / to on rated date (created_at).
Filters:from / to — on the rated day (created_at, org timezone)
Columns
| Field | Type | Description |
|---|---|---|
| id | string | Stable cuid rating id. |
| customer_id | string | Customer that submitted the rating. |
| manifest_id | string | Rated manifest cuid. |
| manifest_number | string | null | Display manifest number (MAN-YY-######); null when the rated manifest is outside the key’s customer scope. |
| on_time | boolean | null | Whether the delivery was on time; null if the rater did not answer. |
| quality_rating | int | Quality score, 1–5. |
| note | string | null | Free-text comment; null if none. |
| created_at | timestamp | ISO 8601 UTC. Primary date for from/to. |
Sample data
| manifest_number | on_time | quality_rating | note | created_at |
|---|---|---|---|---|
| MAN-26-000482 | true | 5 | On time and spotless. | 2026-08-11T16:20:00.000Z |
| MAN-26-000475 | false | 3 | Delivery arrived two hours late. | 2026-08-10T15:05:00.000Z |
| MAN-26-000468 | true | 4 | — | 2026-08-09T14:40:00.000Z |
Example request
curl -s 'https://api.linenflow.co/v1/ratings?from=2026-08-01&to=2026-08-12&limit=50' \
-H 'Authorization: Bearer YOUR_KEY'