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

  1. Create a key: in the customer portal, open Account → API → New Key and copy it — the key is shown once.
  2. Confirm it works:
    bash
    curl -s https://api.linenflow.co/v1/customers \
      -H "Authorization: Bearer YOUR_KEY"
  3. 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"
  4. Connect Excel — see the Power Query recipe. Paste your key, refresh daily.

Authentication

Every request carries your key over HTTPS:

text
Authorization: Bearer lfk_live_…
  • Keys are created in the portal (Account → API) or by your provider.
  • In the examples below, replace YOUR_KEY with your whole key — it already starts with lfk_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 returns 403.
  • 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:

json
{
  "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 currency field. 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:

json
{ "error": { "code": "not_found", "message": "…" }, "request_id": "req_…" }
text
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 support

Rate 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.

  1. Data → Get Data → From Other Sources → Blank Query.
  2. Home → Advanced Editor, paste the code below, set your key on line 2.
  3. Done. If asked how to connect, choose Anonymous — the header authenticates.
  4. Close & Load. Then Data → Refresh All re-pulls any time.

Power Query (M)

text
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
  Table

Want 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

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”.

text
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
GET/api/v1/customers— List the customers this key can access

The 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

FieldTypeDescription
idstringCustomer id (cuid), reused as-is. Pass as the customer_id filter on other endpoints.
namestringCustomer display name.
is_groupbooleanTrue when the customer is a parent group that rolls up child locations; false for a single billed location.
created_attimestampISO 8601 UTC. When the customer record was created.
departmentsarrayArray of {id, name} objects, sorted by name. Full department detail (including cost_center_code) is at /customers/{id}/departments.

Sample data

idnameis_groupcreated_at
cus_grandviewGrandview Medical Centerfalse2026-01-05T14:00:00.000Z
cus_riverbendRiverbend Surgical Centerfalse2026-02-18T16:30:00.000Z
cus_northwindNorthwind Hoteltrue2026-03-02T11:15:00.000Z

Example request

bash
curl -s "https://api.linenflow.co/v1/customers?limit=50" \
  -H "Authorization: Bearer YOUR_KEY"

Customer Departments

any key
GET/api/v1/customers/{id}/departments— List one customer's departments

The departments (cost centers) under one customer. An out-of-scope or unknown customer id returns 404, never 403.

Columns

FieldTypeDescription
idstringDepartment id (cuid).
customer_idstringParent customer id — echoes the {id} in the path.
namestringDepartment display name (e.g. Surgery, ICU).
cost_center_codestring | nullCustomer's accounting cost-center code for the department; null when unset.

Sample data

idcustomer_idnamecost_center_code
dep_surgerycus_grandviewSurgery4400
dep_icucus_grandviewICU4410
dep_housekeepingcus_grandviewHousekeeping—

Example request

bash
curl -s "https://api.linenflow.co/v1/customers/cus_grandview/departments" \
  -H "Authorization: Bearer YOUR_KEY"

Items

any key
GET/api/v1/items— List catalog items relevant to the key's customers

The 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

FieldTypeDescription
idstringItem id (cuid).
skustringCatalog stock-keeping unit / code.
namestringItem display name.
categorystring | nullCatalog category name; null when the item is uncategorized.
unit_of_measurestringCounting/billing unit, e.g. 'each'.
weight_lbdecimal | nullPer-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

idskunamecategoryunit_of_measureweight_lb
itm_bath22BTH-2244-WBath Towel 22x44 WhiteTowelseach0.65
itm_pillowkingPLW-KING-WPillowcase King WhiteLinenseach0.3
itm_gownlgGWN-SRG-LSurgical Gown LargeSurgicaleach1.1

Example request

bash
curl -s "https://api.linenflow.co/v1/items?customer_id=cus_grandview&limit=100" \
  -H "Authorization: Bearer YOUR_KEY"

Manifests

processing
GET/api/v1/manifests— List manifests
GET/api/v1/manifests/{id}— Get one manifest with bins, rewash links, and signature metadata

The 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

FieldTypeDescription
idstringManifest cuid.
manifest_numberstringDisplay number (e.g. MAN-26-088213). Never a lookup key — unique only per plant.
typestringnormal | rewash | new_linen.
statusstringcollected | received | cleaned | staged | delivered.
customer_idstringOwning customer cuid.
department_idstring | nullnull for customer-wide manifests.
department_namestring | nullnull when there is no department.
created_attimestampISO 8601 UTC. Manifest created (collected).
picked_up_attimestamp | nullISO 8601 UTC, or null.
received_attimestamp | nullISO 8601 UTC, or null.
cleaned_attimestamp | nullISO 8601 UTC, or null.
staged_attimestamp | nullISO 8601 UTC, or null.
delivered_attimestamp | nullISO 8601 UTC, or null. Primary date; from/to filter on this.
soil_weight_lbdecimal | nullPounds, rounded to 0.1. Soil-side net weight.
clean_weight_lbdecimal | nullPounds, rounded to 0.1. Clean-side net weight.
clean_piece_countint | nullCounted clean pieces, or null.
variance_pctdecimal | null(soil − clean) ÷ soil; positive = normal wash loss, negative when clean > soil.
variance_within_toleranceboolean | nullWhether variance is inside the org tolerance.
parent_manifest_idstring | nullSet on rewash manifests (mother/child link).
amount_centsint | nullInteger cents; snapshot of the billed amount.
currencystringAlways 'usd'.
binsarrayDetail only (manifests/{id}). Receiving (soil) + shipping (clean) bins; each has side, content, gross_lb, tare_lb, net_lb, and item lines.
held_rewashobject | nullDetail only. Held-back rewash summary, or null. soil_rewash_lb / error_rewash_lb are null when the org hides the fault split.
rewash_linksobjectDetail only. { parent_manifest: {id, manifest_number} | null, rewash_children: [{id, manifest_number}] }.
signatureobjectDetail only. Delivery signature metadata: { signed_by_name, signed_by_title, delivered_at }. Never geo or image keys.

Sample data

manifest_numbertypestatusdelivered_atsoil_weight_lbclean_weight_lbvariance_pct
MAN-26-088213normaldelivered2026-08-11T14:22:00.000Z118.4108.20.086
MAN-26-088214normaldelivered2026-08-11T15:48:00.000Z96.390.10.064
MAN-26-088180rewashdelivered2026-08-10T13:10:00.000Z22.521.80.031

Example request

bash
curl "https://api.linenflow.co/v1/manifests?status=delivered&type=normal&limit=50" \
  -H "Authorization: Bearer YOUR_KEY"

Daily Deliveries

processing
GET/api/v1/deliveries/daily— Daily delivered counts by customer, department, and item with par and fill rate

Per 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

FieldTypeDescription
datestringDelivery day (org timezone), YYYY-MM-DD.
customer_idstringCustomer cuid.
customer_namestringCustomer display name.
department_idstring | nullnull for customer-level rows.
department_namestring | nullnull for customer-level rows.
item_idstringItem cuid.
item_namestringItem display name.
delivered_countintSum of cleaned pieces delivered that day; held rewash and damage pieces are excluded.
daily_parint | nullLive pieces par (department row, falling back to customer-wide); null when there is no par or the par is in cart_loads.
fill_ratedecimal | nulldelivered_count ÷ daily_par, 3 decimals; null without a usable pieces par.

Sample data

datecustomer_namedepartment_nameitem_namedelivered_countdaily_parfill_rate
2026-08-11Grandview Medical CenterSurgeryBath Towel 22x44 White3003600.833
2026-08-11Riverbend Surgical CenterICUPillowcase King White2402201.091
2026-08-10Northwind HotelHousekeepingBar Mop White480——

Example request

bash
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
GET/api/v1/inventory/totals— List per-item inventory totals by location

Per-item inventory rollup across every location: total owned plus at-customer, at-facility, in-transit, and retired counts.

Columns

FieldTypeDescription
item_idstringItem cuid.
item_namestringItem display name.
total_ownedintat_customer + at_facility + in_transit (retired units excluded).
at_customerintUnits at the customer or in service.
at_facilityintUnits new, in reserve, or in plant.
in_transitintUnits in transit.
retiredintUnits in any retired state.

Sample data

item_iditem_nametotal_ownedat_customerat_facilityin_transitretired
itm_bath22Bath Towel 22x44 White420026001400200340
itm_pillowkingPillowcase King White310019001050150210
itm_gownlgSurgical Gown Large9005403006075

Example request

bash
curl "https://api.linenflow.co/v1/inventory/totals?customer_id=cus_grandview" \
  -H "Authorization: Bearer YOUR_KEY"

Par Levels

processing
GET/api/v1/inventory/par— List live par levels

Live 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

FieldTypeDescription
customer_idstringCustomer cuid.
department_idstring | nullnull = the customer-wide par row.
department_namestring | nullnull for customer-wide rows.
item_idstringItem cuid.
item_namestringItem display name.
par_quantityintTarget quantity in par_unit.
par_unitstringpieces | cart_loads.
reorder_thresholdintReorder trigger level; 0 when unset.

Sample data

item_namedepartment_namepar_quantitypar_unitreorder_threshold
Bath Towel 22x44 WhiteSurgery360pieces120
Pillowcase King WhiteICU220pieces80
Bar Mop White—30cart_loads8

Example request

bash
curl "https://api.linenflow.co/v1/inventory/par?customer_id=cus_grandview&department_id=dep_surgery" \
  -H "Authorization: Bearer YOUR_KEY"

Units

processing
GET/api/v1/inventory/units— List units

The floors and areas that hold carts. A key whose user counts only some units reads only those units.

Columns

FieldTypeDescription
idstringUnit cuid.
customer_idstringCustomer cuid.
namestringUnit name.
buildingstring | nullBuilding, when recorded.
floorstring | nullFloor, when recorded.
unit_typestring | nullFree-form type, e.g. med_surg.
cost_centerstring | nullThe customer’s cost center code.
cart_countintCarts in the unit.
is_activebooleanfalse = retired unit.

Sample data

namebuildingfloorcost_centercart_countis_active
8 North EastMain8701103true
Surgery PACUMain2704502true

Example request

bash
curl "https://api.linenflow.co/v1/inventory/units?customer_id=cus_grandview" \
  -H "Authorization: Bearer YOUR_KEY"

Carts

processing
GET/api/v1/inventory/carts— List carts with their par

Every cart or closet of a unit with its live par sheet and the days it is counted.

Filters:unit_id

Columns

FieldTypeDescription
idstringCart cuid.
customer_idstringCustomer cuid.
unit_idstringUnit cuid.
unit_namestring | nullUnit name.
namestringCart name.
kindstringcart | closet | linen_room | exchange_cart.
stocking_methodstringfixed | census.
count_daysarraysun … sat — the days the cart is counted.
is_activebooleanfalse = retired cart.
pararrayitem_id · item_name · par_quantity, live rows only.

Sample response

json
{
  "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

bash
curl "https://api.linenflow.co/v1/inventory/carts?customer_id=cus_grandview&unit_id=unit_8ne" \
  -H "Authorization: Bearer YOUR_KEY"

Cart Counts

processing
GET/api/v1/inventory/counts— List cart counts

The 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

FieldTypeDescription
idstringCount cuid.
customer_idstringCustomer cuid.
cart_idstringCart cuid.
cart_namestring | nullCart name.
unit_idstring | nullUnit cuid.
unit_namestring | nullUnit name.
datedateOrg-local count day.
statusstringpending | in_progress | counted | verified | assumed.
unscheduledbooleantrue = counted on a day the cart is not due.
counted_attimestamp | nullWhen the count was marked done.
verified_attimestamp | nullWhen the laundry verified it.
linesarrayitem_id · item_name · par · on_hand · required · delivered · extra · returned.

Sample response

json
{
  "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

bash
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
GET/api/v1/inventory/balances— List balances by item and status

Stock by item and status, derived from the movement ledger. One row per item and status with a non-zero quantity.

Filters:item_id

Columns

FieldTypeDescription
item_idstringItem cuid.
item_namestringItem display name.
statusstringnew | in_reserve | in_plant | in_transit | at_customer | retired_*.
quantityintPieces in that status.

Sample data

item_namestatusquantity
Bath Towel 22x44 Whitein_reserve350
Bath Towel 22x44 Whiteat_customer800
Bath Towel 22x44 Whiteretired_lost30

Example request

bash
curl "https://api.linenflow.co/v1/inventory/balances?customer_id=cus_grandview" \
  -H "Authorization: Bearer YOUR_KEY"

Inventory Signals

processing
GET/api/v1/inventory/signals— List inventory signals

The 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

FieldTypeDescription
idstringSignal cuid.
kindstringshortfall_streak | rolling_erosion | injection_dependency | over_fill | dormant_stock | circulation_breach | unmatched_injection | …
severitystringlow | medium | high.
statusstringopen | acknowledged | dismissed | resolved.
customer_idstring | nullCustomer cuid.
department_idstring | nullDepartment cuid, when the signal is per department.
item_idstring | nullItem cuid; null = all items.
item_namestring | nullItem display name.
summarystringOne line describing the condition.
suggested_order_qtyint | nullCirculation breaches only; rounded to the pack size.
window_startdateFirst day of the window the signal read.
window_enddateLast day of the window.
generated_attimestampThe last nightly run that confirmed it.

Sample data

kindseverityitem_namesummarystatus
shortfall_streakhighBath Towel 22x44 White7 service days under 85% · average 70%open
dormant_stocklowBar Mop WhiteNo delivery in 60 days against a par of 30acknowledged

Example request

bash
curl "https://api.linenflow.co/v1/inventory/signals?customer_id=cus_grandview&kind=shortfall_streak" \
  -H "Authorization: Bearer YOUR_KEY"

New Linen

processing
GET/api/v1/new-linen— List new-linen injections

New-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

FieldTypeDescription
idstringManifest cuid.
manifest_numberstringDisplay number (e.g. MAN-26-088260).
datestring | nullOrg-local cleaned day (YYYY-MM-DD); null when not yet cleaned.
customer_idstringCustomer cuid.
department_idstring | nullnull for customer-wide injections.
directionstringAlways 'in' (injections; the model has no outbound).
soil_weight_lbdecimal | nullPounds, rounded to 0.1. Soil weigh-in.
injected_weight_lbdecimal | nullPounds, rounded to 0.1. Clean injected weight.
itemsarrayPer-item injected counts: [{ item_id, item_name, piece_count }].

Sample data

manifest_numberdatedirectionsoil_weight_lbinjected_weight_lb
MAN-26-0882602026-08-10in60.459.8
MAN-26-0881882026-08-08in42.141.7
MAN-26-0880902026-08-05in8887.2

Example request

bash
curl "https://api.linenflow.co/v1/new-linen?from=2026-08-01&to=2026-08-11" \
  -H "Authorization: Bearer YOUR_KEY"

Rewash

processing
GET/api/v1/rewash— List held and resolved rewash batches

Held 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

FieldTypeDescription
idstringPending-rewash cuid.
customer_idstringCustomer cuid.
department_idstring | nullnull for customer-wide batches.
discovered_attimestampISO 8601 UTC (created date). Primary date; from/to filter on this.
statusstringpending | consumed | voided.
held_weight_lbdecimal | nullPounds, rounded to 0.1. Override-aware total held weight.
soil_rewash_lbdecimal | nullPounds. null when the org hides the fault split from customers.
error_rewash_lbdecimal | nullPounds. null when the org hides the fault split from customers.
parent_manifestobject{ id, manifest_number } of the mother manifest.
consumed_by_manifestobject | null{ id, manifest_number, delivered_at } once consumed, else null.
outcomesarrayFault outcomes: [{ fault_class, good_lb, damage_lb }].

Sample data

idstatusdiscovered_atheld_weight_lbsoil_rewash_lberror_rewash_lb
rw_51204consumed2026-08-09T14:20:00.000Z62.440.122.3
rw_51330pending2026-08-11T08:12:00.000Z15.69.46.2
rw_51188voided2026-08-07T16:40:00.000Z853

Example request

bash
curl "https://api.linenflow.co/v1/rewash?status=consumed&from=2026-08-01&to=2026-08-11" \
  -H "Authorization: Bearer YOUR_KEY"

Invoices

accounting
GET/api/v1/invoices— List sent invoices with totals, balance, and a signed PDF link

Sent, 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

FieldTypeDescription
idstringInvoice id (cuid). Stable lookup key.
invoice_numberstringDisplay document number (PREFIX-date-sequence, e.g. INV-26-00218). Unique per org but a display field — never a lookup key.
statusstringOne of sent, partially_paid, paid. These are the only statuses that ever appear publicly; drafts, void, and written-off are never returned.
customer_idstringOwning customer id (cuid).
department_idstring | nullSet when the invoice is scoped to a single department; null for a consolidated invoice spanning departments.
issued_attimestampISO 8601 UTC. The issued date (this list’s from/to primary date).
due_attimestamp | nullISO 8601 UTC payment due date; null when the customer has no payment terms set.
sent_attimestampISO 8601 UTC. When the invoice was sent — always present, since only sent/partially_paid/paid invoices appear.
paid_attimestamp | nullISO 8601 UTC. When the invoice was paid in full; null until then.
subtotal_centsintInteger cents, before surcharges and tax.
surcharges_centsintInteger cents of surcharges.
tax_centsintInteger cents of tax.
total_centsintInteger cents. The invoice total (subtotal + surcharges + tax).
balance_centsintInteger cents still outstanding; 0 once paid.
currencystringISO 4217 currency, lowercase (e.g. usd).
pdf_urlstringSigned 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_numberstatusissued_atdue_attotal_centsbalance_cents
INV-26-00218sent2026-08-01T14:30:00.000Z2026-08-31T14:30:00.000Z127500127500
INV-26-00219partially_paid2026-07-15T15:00:00.000Z2026-08-14T15:00:00.000Z342000162000
INV-26-00220paid2026-07-01T13:45:00.000Z2026-07-31T13:45:00.000Z899000

Example request

bash
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
GET/api/v1/payments— List payments with their invoice allocations

Customer 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

FieldTypeDescription
idstringPayment id (cuid).
customer_idstringPaying customer id (cuid).
kindstringPayment method: one of card, ach, check, cash, credit_memo, write_off.
statusstringOne of settled, returned, failed.
amount_centsintInteger cents received.
currencystringISO 4217 currency, lowercase (e.g. usd).
received_attimestampISO 8601 UTC. The received date (this list’s from/to primary date).
returned_attimestamp | nullISO 8601 UTC. Set when the payment was returned or reversed; null otherwise.
allocationsarrayOne 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

idkindstatusamount_centsreceived_at
pay_88012checksettled1275002026-08-10T14:00:00.000Z
pay_88013achsettled2220002026-08-05T16:20:00.000Z
pay_88014cardreturned500002026-07-28T13:10:00.000Z

Example request

bash
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
GET/api/v1/balance— A/R balance and aging buckets per customer

Open 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

FieldTypeDescription
customer_idstringCustomer id (cuid).
customer_namestringThe customer’s name.
balance_centsintInteger cents. Total open balance across the customer’s sent and partially_paid invoices.
currencystringISO 4217 currency, lowercase (e.g. usd).
agingobjectInteger-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_namebalance_cents
Grandview Medical Center127500
Riverbend Surgical Center162000

Example request

bash
curl -s "https://api.linenflow.co/v1/balance?customer_id=cus_grandview" \
  -H "Authorization: Bearer YOUR_KEY"

Purchase Orders

accounting
GET/api/v1/purchase-orders— List purchase orders with total, spent, and remaining amounts

Customer 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

FieldTypeDescription
idstringPurchase order id (cuid).
customer_idstringOwning customer id (cuid).
po_numberstringThe customer’s PO number (display field).
statusstringOne of pending, active, closed, cancelled.
total_centsintInteger cents. The PO’s authorized budget.
spent_centsintInteger cents drawn down so far (= total_cents − remaining_cents).
remaining_centsintInteger cents left on the PO.
currencystringISO 4217 currency, lowercase (e.g. usd).
started_attimestampISO 8601 UTC. The start date (this list’s from/to primary date).
expires_attimestampISO 8601 UTC. When the PO expires.

Sample data

po_numberstatustotal_centsspent_centsremaining_centsexpires_at
PO-2026-118active5000000127500037250002027-06-30T04:00:00.000Z
PO-2026-092pending2500000025000002027-03-31T04:00:00.000Z
PO-2025-441closed1200000120000002026-06-30T04:00:00.000Z

Example request

bash
curl -s "https://api.linenflow.co/v1/purchase-orders?status=active" \
  -H "Authorization: Bearer YOUR_KEY"

Invoice PDF

accounting
GET/api/v1/files/invoice/{id}— Fetch one invoice PDF via its signed link

Download 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

FieldTypeDescription
(binary)application/pdfThe 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

json
"Returns application/pdf bytes, not JSON. Fetch the signed pdf_url from GET /invoices and save the response to a file."

Example request

bash
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
GET/api/v1/orders— List direct-sales orders
GET/api/v1/orders/{id}— Fetch one order with lines and charges

Direct-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

FieldTypeDescription
idstringStable cuid order id. Use for orders/{id}.
order_numberstringDisplay document number (DS-YY-#####). Display field, never a lookup key.
stagestringOne of draft, submitted, in_production, shipped, delivered, cancelled.
customer_idstringOwning customer cuid.
placed_attimestamp | nullISO 8601 UTC. When the order was submitted; null on drafts. Primary date for from/to.
requested_delivery_datetimestamp | nullISO 8601 UTC. Delivery the customer asked for; null if unset.
confirmed_delivery_datetimestamp | nullISO 8601 UTC. Delivery LinenFlow committed to; null until confirmed.
delivered_attimestamp | nullISO 8601 UTC. Null until the order reaches the delivered stage.
subtotal_centsintInteger cents. Sum of the line subtotals, before order-level charges.
total_centsintInteger cents. Order total including order-level charges.
currencystringAlways 'usd'.
orderer_namestring | nullPerson who placed the order; null if unrecorded.
wearer_namestring | nullWearer first + last, trimmed; null when both are empty.
wearer_departmentstring | nullFree-text wearer department (order's own field, not a linked department id); null if unset.
delivery_locationstring | nullFree-text delivery location; null if unset.
po_numberstring | nullCustomer PO reference; null if none.
line_countintNumber of lines on the order.

Sample data

order_numberstageplaced_atwearer_nametotal_centsline_count
DS-26-00042in_production2026-08-03T14:10:00.000ZElena Ruiz288002
DS-26-00039delivered2026-07-28T11:05:00.000ZMarcus Bell154001
DS-26-00051submitted2026-08-10T16:30:00.000ZPriya Nair96001

Example request

bash
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
GET/api/v1/quality-tickets— List quality tickets

Quality 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

FieldTypeDescription
idstringStable cuid ticket id.
ticket_numberstringDisplay document number (TKT-YY-#####). Display field, not a lookup key.
kindstringRaw kind: stain, rip_damage, shortage, wrong_item, late_delivery, billing_dispute, other (legacy rows may carry complaint, missing_linen, damage_claim).
kind_labelstringHuman label for kind, e.g. 'Stain', 'Rip / damage', 'Billing question'.
statusstringOne of new, acknowledged, in_progress, resolved, verified.
status_labelstringHuman label: 'Submitted', 'Acknowledged', 'In progress', 'Resolved' (verified also shows 'Resolved').
subjectstringShort ticket subject line.
created_attimestampISO 8601 UTC. Primary date for from/to.
acknowledged_attimestamp | nullISO 8601 UTC. When staff first acknowledged; null until then.
resolved_attimestamp | nullISO 8601 UTC. When the ticket was resolved; null while open.
resolution_notestring | nullHow it was resolved; null while open.
last_correspondence_attimestamp | nullISO 8601 UTC. Timestamp of the latest reply on the thread; null if none.
customer_visible_eventsarrayCustomer-visible thread entries only (internal notes excluded), oldest first. Each: { event_type, actor_kind, body (string|null), created_at }.

Sample data

ticket_numberkind_labelstatus_labelsubjectcreated_at
TKT-26-00018StainAcknowledgedRust stains on ICU bath towels2026-08-10T13:20:00.000Z
TKT-26-00015ShortageResolvedShort 40 pillowcases on Housekeeping cart2026-08-07T09:05:00.000Z
TKT-26-00021Late deliveryIn progressSurgery delivery arrived two hours late2026-08-11T15:40:00.000Z

Example request

bash
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
GET/api/v1/ratings— List delivery ratings

Per-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

FieldTypeDescription
idstringStable cuid rating id.
customer_idstringCustomer that submitted the rating.
manifest_idstringRated manifest cuid.
manifest_numberstring | nullDisplay manifest number (MAN-YY-######); null when the rated manifest is outside the key’s customer scope.
on_timeboolean | nullWhether the delivery was on time; null if the rater did not answer.
quality_ratingintQuality score, 1–5.
notestring | nullFree-text comment; null if none.
created_attimestampISO 8601 UTC. Primary date for from/to.

Sample data

manifest_numberon_timequality_ratingnotecreated_at
MAN-26-000482true5On time and spotless.2026-08-11T16:20:00.000Z
MAN-26-000475false3Delivery arrived two hours late.2026-08-10T15:05:00.000Z
MAN-26-000468true4—2026-08-09T14:40:00.000Z

Example request

bash
curl -s 'https://api.linenflow.co/v1/ratings?from=2026-08-01&to=2026-08-12&limit=50' \
  -H 'Authorization: Bearer YOUR_KEY'