Public API Order Hub — Orders & Order Lines Guide
How to manage orders and their line items on the Retraced platform via the Public API.
The Order Hub exposes orders and their line items:
- Orders (
/api/v2/orders) — the commercial document: order number, buyer, supplier, dates, status, and its line items. - Order lines (
/api/v2/order-lines) — the line items on an order (which style, how much, in what unit), exposed read-only (GETonly).
An order line only ever exists as part of an order, so all line writes happen through the order: create lines nested in POST /orders, and add/change/remove them later with PATCH /orders/:id. Deleting an order deletes its lines. You read lines back via the GET /order-lines endpoints.
Migration note: the previous
POST/PATCH/DELETE /api/v2/order-linesendpoints have been removed. Manage lines via the order endpoints described below. TheGETline endpoints are unchanged.
Setup
# Base URL
BASE_URL="https://publicapi.retraced.com/api/v2"
# Authentication — use your company API key
API_KEY="your-company-api-key"
All examples use curl. The API key is passed via the companyapikey header. Every operation is scoped to the company the API key belongs to — you can only see and manage your own orders.
Orders
Create an order
curl -X POST "$BASE_URL/orders" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"orderNumber": "PO-2026-0042",
"status": "IN_PROGRESS",
"orderSubType": "STANDARD",
"source": "ERP",
"tags": ["priority"],
"buyerCompanyId": "<buyer-company-id>",
"supplierCompanyId": "<supplier-company-id>",
"receiverCompanyId": "<receiver-company-id>",
"vendorCompanyId": "<vendor-company-id>",
"factory": "<factory-company-id>",
"buyerFacilityProcess": "Quality inspection",
"supplierFacilityProcess": "Weaving",
"receiverFacilityProcess": "Washing & finishing",
"orderDate": "2026-07-01",
"deliveryDate": "2026-09-15",
"agreedDeliveryDate": "2026-09-10",
"expectedShippingDate": "2026-08-28",
"shippingDate": "2026-08-30",
"expectedDeliveryDate": "2026-09-15",
"shipmentReference": "SHIP-2026-0042",
"internalResponsiblePersons": ["<user-id-or-email>"],
"season": "FW26",
"counterpartyOrderNumberRef": "WW-SO-2026-118",
"lines": [
{
"orderLineNumber": "PO-2026-0042-PACKAGING",
"styleId": "<style-id>",
"quantity": 300,
"unit": "PIECES",
"secondaryQuantity": 120.5,
"secondaryUnit": "KILOGRAM"
}
]
}'
Only orderNumber, buyerCompanyId and supplierCompanyId are required — every other field can be omitted. Nested lines are created atomically with the order and are returned in the create response (and on GET /orders/:id), each carrying its server-assigned id and lineNo.
- The order owner must be a party to the order: your authenticated company must be the
buyerCompanyIdor thesupplierCompanyId, otherwise the request is rejected with400(codeorder_owner_not_party). - Receiver defaults to the buyer: if you omit
receiverCompanyId, it defaults tobuyerCompanyId(andreceiverFacilityProcessdefaults tobuyerFacilityProcess). If you send one ofreceiverCompanyId/receiverFacilityProcessyou must send both (400otherwise) — this pairing rule applies on create only. orderTypeandtracingStatusmay be present in the body for convenience but are ignored — both are computed by the server.
Request body fields:
| Field | Type | Required | Notes |
|---|---|---|---|
orderNumber |
string | yes | 1–255 characters (exact match — no trimming or case folding). Must be unique within your company for the same order type — a duplicate returns 400 with code order_number_already_existing. |
lines |
array | no | Nested order lines, created atomically with the order (a failing line rolls the whole request back). Each entry takes the per-line fields minus id. May be omitted or empty; lines can also be added later via PATCH /orders/:id. |
buyerCompanyId |
string | yes | Company ID of the buyer (max 36 chars). |
supplierCompanyId |
string | yes | Company ID of the supplier (max 36 chars). |
orderSubType |
enum | no | STANDARD, REPEAT, COMMIT, SAMPLE or EXTRACTION_SPLIT. |
status |
enum | no | IN_PROGRESS, IN_REVIEW or COMPLETED. |
tags |
string[] | no | Free-form tags. |
source |
string | no | Where the order originated (max 255 chars). |
receiverCompanyId / vendorCompanyId / factory |
string | no | Additional party company IDs (max 36 chars each). |
buyerFacilityProcess / supplierFacilityProcess / receiverFacilityProcess |
string | no | Facility process references (max 255 chars each). |
orderDate, deliveryDate, agreedDeliveryDate, expectedShippingDate, shippingDate, expectedDeliveryDate, actualDeliveryDate |
date | no | ISO calendar date, YYYY-MM-DD. |
shipmentReference |
string | no | Max 255 chars. |
internalResponsiblePersons |
string[] | no | Email addresses or user IDs of users in your company — the server resolves each email to a user ID before saving. An unknown email/ID returns 404 (responsible_person_not_found). |
internalResponsiblePerson |
string | no | Deprecated — use internalResponsiblePersons. Accepts a single email/user ID, auto-mapped to a one-element internalResponsiblePersons. Do not send both fields (400). An empty string is rejected with 400. |
season |
string | no | e.g. FW26. |
counterpartyOrderNumberRef |
string | no | Your counterparty's own order number reference. |
orderTypeis computed by the server — you never send it. It is derived from how the buyer/supplier relate to your (authenticated) company: you are the buyer →PURCHASE_ORDER, you are the supplier →SALES_ORDER, you are both →INTERNAL_ORDER.
Response (200):
{
"metadata": { "success": true },
"data": {
"id": "order_01KWHF7TD318JZA3KM9A9E9979",
"forCompanyId": "<your-company-id>",
"orderNumber": "PO-2026-0042",
"orderType": "PURCHASE_ORDER",
"orderSubType": "STANDARD",
"status": "IN_PROGRESS",
"tags": ["priority"],
"source": "ERP",
"buyerCompanyId": "<buyer-company-id>",
"supplierCompanyId": "<supplier-company-id>",
"receiverCompanyId": "<receiver-company-id>",
"vendorCompanyId": "<vendor-company-id>",
"factory": "<factory-company-id>",
"buyerFacilityProcess": "Quality inspection",
"supplierFacilityProcess": "Weaving",
"receiverFacilityProcess": "Washing & finishing",
"orderDate": "2026-07-01",
"deliveryDate": "2026-09-15",
"agreedDeliveryDate": "2026-09-10",
"expectedShippingDate": "2026-08-28",
"shippingDate": "2026-08-30",
"expectedDeliveryDate": "2026-09-15",
"actualDeliveryDate": null,
"shipmentReference": "SHIP-2026-0042",
"internalResponsiblePersons": ["<user-id>"],
"internalResponsiblePerson": "<user-id>",
"season": "FW26",
"counterpartyOrderNumberRef": "WW-SO-2026-118",
"isArchived": false,
"tracingStatus": null,
"createdAt": "2026-07-02T10:30:00.000Z",
"createdByApiKeyId": "<api-key-id>",
"updatedAt": "2026-07-02T10:30:00.000Z",
"lineCount": 1,
"lines": [
{
"id": "ordline_01KWHF7XAY4Q74CAVXYAPJ6FJV",
"orderLineNumber": "PO-2026-0042-PACKAGING",
"lineNo": 1,
"styleId": "<style-id>",
"quantity": 300,
"unit": "PIECES",
"secondaryQuantity": 120.5,
"secondaryUnit": "KILOGRAM",
"tracingReflectionOrderId": null,
"tracingOrderRequestedAt": null
}
]
}
}
(Response trimmed — every order also carries full createdBy* / updatedBy* audit fields. Fields you never sent come back as null; tracingStatus is system-computed and read-only.)
internalResponsiblePerson(singular) is a deprecated read-only mirror ofinternalResponsiblePersons[0](ornullwhen there are none); prefer the array.lineCountis the number of lines on the order.linesis the full nested line array onPOST /ordersandGET /orders/:id, but is always[]onGET /orders(list) — useGET /order-lines?orderId=...to page a list's lines.
Store data.id — you need it to manage order lines and for later updates. Order ids are server-generated with an order_ prefix (order lines use ordline_); treat them as opaque strings.
The party fields (who's who)
An order carries up to five company references. Only buyer and supplier are required and enforced (your company must be one of them); the rest are optional descriptive parties. How brands typically use them:
| Field | Typically represents |
|---|---|
buyerCompanyId |
The company placing the order. |
supplierCompanyId |
The company receiving the order — the agent or supplier on the other side of it. |
receiverCompanyId |
Who takes delivery of the goods. Defaults to the buyer when omitted. |
vendorCompanyId |
An intermediary between the agent and the factory, when one is involved. |
factory |
The company / site where the goods are actually manufactured. |
The *FacilityProcess fields (buyerFacilityProcess, supplierFacilityProcess, receiverFacilityProcess) are free-text labels for the process each party performs — they are descriptive, not validated against a list.
These are conventions, not rules the platform enforces. Retraced only checks that your company is the buyer or supplier and that the receiver pair is supplied together (see Create an order); it assigns no other meaning to these fields.
Order status
status (IN_PROGRESS, IN_REVIEW, COMPLETED) is not tied to any Retraced workflow — the platform never acts on it or transitions it for you. Use it to carry whatever state your own process needs.
Date fields
Business dates (orderDate, agreedDeliveryDate, deliveryDate, …) are calendar dates in YYYY-MM-DD with no time or timezone, stored and returned verbatim; createdAt / updatedAt are ISO 8601 UTC timestamps set by the server. See Dates & time for the full rules.
Archiving vs. deleting
An order can be archived (reversible) or deleted (permanent) — they are different:
- Archive via
"isArchived": trueonPATCH /orders/:id(unarchive withfalse). The order and its lines are kept intact but drop out of the default list and are excluded from the analytics and reporting views in the Retraced platform. Filter withisArchived=true/false; omit the parameter to list both archived and active orders. - Delete via
DELETE /orders/:idis a hard delete — the order and all its lines are permanently removed and can't be recovered.
List orders
curl "$BASE_URL/orders?page=1&limit=20&sort=updatedAt&order=DESC&status=IN_PROGRESS" \
-H "companyapikey: $API_KEY"
Query parameters:
| Parameter | Type | Default | Notes |
|---|---|---|---|
page |
number | 1 |
1-based page number. |
limit |
number | 20 |
Page size, 1–100. |
sort |
enum | updatedAt |
updatedAt, createdAt, orderNumber, orderDate or status. |
order |
enum | desc |
ASC or DESC. |
type |
string | — | Filter by computed order type, e.g. PURCHASE_ORDER. |
supplier / buyer |
string | — | Filter by supplier / buyer company ID. |
status |
string | — | Filter by status. |
tag |
string | — | Filter by a single tag. |
orderNumber |
string | — | Filter by order number. |
isArchived |
true | false |
— | Filter archived / non-archived orders. |
updatedAfterUnixMs / updatedBeforeUnixMs |
number | — | Unix timestamps in milliseconds; useful for incremental syncs. |
Deleted orders are physically removed and never returned.
Response (200):
{
"metadata": {
"success": true,
"pagination": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }
},
"data": [ { "id": "order_01KWHF7TD318JZA3KM9A9E9979", "orderNumber": "PO-2026-0042", "...": "..." } ]
}
Fetch, update, delete
# Fetch one order
curl "$BASE_URL/orders/<order-id>" -H "companyapikey: $API_KEY"
# Partial update — send only the fields you want to change
curl -X PATCH "$BASE_URL/orders/<order-id>" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "COMPLETED", "actualDeliveryDate": "2026-09-12" }'
# Delete (hard — also deletes the order's lines)
curl -X DELETE "$BASE_URL/orders/<order-id>" -H "companyapikey: $API_KEY"
PATCHhas partial semantics: omitted fields keep their value. Nullable fields (dates,source,season, party IDs, …) can be cleared by sendingnull. You can also archive/unarchive via"isArchived": true|false.- Sending a
linesarray reconciles the order's lines — see Update an order's lines. - Changing
orderNumber— or changingbuyerCompanyId/supplierCompanyIdin a way that re-derives the order type — returns400(codeorder_number_already_existing) if the number is already taken in the resulting (company, order type) group. DELETEis a hard delete (returns204): the order and all of its lines are permanently removed — this cannot be undone. Deleting an already-deleted or foreign order returns404.- Tracing side-effects: if the order (or any of its lines) is part of a tracing chain, a
PATCHre-syncs the linked tracing orders and notifies the counterparty, and aDELETEcascades to remove the counterparty's mirrored tracing data. NestedstyleIds must reference a non-archived style your company owns, otherwise the write is rejected.
Update an order's lines
Send a lines array on PATCH /orders/:id to declaratively full-sync the order's lines — the array is the desired final set:
- an entry with an
idupdates that existing line (only the fields you include change); - an entry without an
idcreates a new line (styleId,quantity,unitare then required); - any existing line absent from the array is deleted.
Omitting lines entirely leaves the order's lines untouched. Because omission means deletion, always send the complete current set of lines you want to keep — fetch them first via GET /order-lines?orderId=....
curl -X PATCH "$BASE_URL/orders/<order-id>" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "IN_REVIEW",
"lines": [
{ "id": "ordline_01KWHF7XAY4Q74CAVXYAPJ6FJV", "quantity": 1750 },
{ "id": "ordline_01KWHF7YSW1A1R34E1DMEHMAK5", "secondaryQuantity": null },
{ "styleId": "<style-id>", "quantity": 250, "unit": "PIECES" }
]
}'
In the example the first line is updated, the second clears its secondary quantity (which also clears secondaryUnit), a third line is created, and any other existing line is deleted. The order's scalar fields and its lines are reconciled atomically — if any line fails (e.g. a duplicate orderLineNumber, 409), the whole PATCH rolls back. A bare { "id": "..." } entry keeps that line unchanged (its audit fields are not touched). Fetch the resulting lines with GET /order-lines?orderId=....
See the order line fields for the accepted per-line properties.
Order lines (read-only)
Order lines are created, updated, and deleted only through their parent order (see Create an order and Update an order's lines). Over the Public API they are exposed read-only.
Order line fields
The per-line object used inside a create/update lines array:
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string | on update | The line's ordline_ id. Present → update that line; absent → create a new line. |
styleId |
string | on create | ID of a style owned by your company (max 36 chars), else 404. Required when creating (no id). |
quantity |
number | on create | Positive; stored with 2 decimal places. Required when creating. |
unit |
enum | on create | Unit of measure for quantity — one of the unit values below (e.g. PIECES, KILOGRAM, METRE). Required when creating. |
orderLineNumber |
string | no | 1–255 chars. Must be unique within the order. If omitted on create, auto-generated as {orderNumber}-{orderType}-L{lineNo} (e.g. PO-2026-0042-PURCHASE_ORDER-L1). |
secondaryQuantity |
number | null | no | Optional second measurement (positive, 2 decimal places). Always a weight in kilograms. Send null on update to clear it. |
secondaryUnit |
enum | null | no | Always KILOGRAM — the only accepted value. You can omit it: whenever a secondaryQuantity is set the server sets secondaryUnit to KILOGRAM, and clearing the quantity clears the unit too. |
lineNois assigned by the server (1, 2, 3, … per order) and never accepted as input. New lines continue numbering after the order's existing lines. AnorderLineNumberthat already exists on the order — or that appears twice in one request — returns409with codeorder_not_unique, and the whole write rolls back.
Unit values
unit accepts the unit enum below. Any other value is rejected with 400.
| Group | Values |
|---|---|
| Count / textile | PIECES, PAIR, PERCENTAGE, DENIER, JUTE, ENGLISH_COUNT, METRIC_COUNT, WORSTED_COUNT, TEX |
| Weight / mass | MILLIGRAM, GRAM, KILOGRAM, METRIC_TON, POUNDS, OUNCE, STONE, SHORT_TON, LONG_TON, HUNDREDWEIGHT, SHORT_HUNDREDWEIGHT, LONG_HUNDREDWEIGHT, DRAM, GRAIN, PENNYWEIGHT, SCRUPLE, GRAM_PER_SQUARE_METER, GRAM_PER_SQUARE_FOOT, GRAM_PER_SQUARE_YARD, OUNCE_PER_SQUARE_YARD |
| Length | MILLIMETRE, CENTIMETRE, DECIMETRE, METRE, INCH, FOOT, YARD, MILE, NAUTICAL_MILE, FURLONG, ROD, FATHOM |
| Area | SQUARE_CENTIMETRE, SQUARE_DECIMETRE, SQUARE_METRE, SQUARE_INCH, SQUARE_FEET, SQUARE_YARD, SQUARE_ROD, SQUARE_MILE, ACRE |
| Volume | MILLILITRE, CENTILITRE, LITRE, CUBIC_DECIMETRE, CUBIC_METRE, CUBIC_INCH, CUBIC_FOOT, CUBIC_YARD, GALLON, QUART, PINT, GILL, FLUID_OUNCE, FLUID_DRAM, MINIM, BUSHEL, PECK, CORD, ACRE_FOOT |
| Other | KILOWATT_HOUR, MOUND |
List order lines
# Lines of one order
curl "$BASE_URL/order-lines?orderId=<order-id>&page=1&limit=20" \
-H "companyapikey: $API_KEY"
# All lines across all your orders (omit orderId)
curl "$BASE_URL/order-lines?page=1&limit=100" -H "companyapikey: $API_KEY"
Pagination works exactly like orders (page 1-based, limit 1–100 default 20, same metadata.pagination envelope). Lines of deleted orders are never returned.
Each line additionally carries two read-only tracing fields — tracingReflectionOrderId (the tracing order this line is reflected into, or null) and tracingOrderRequestedAt (null until tracing is requested) — plus an embedded order summary object (id, orderNumber, orderType, buyerCompanyId, supplierCompanyId, orderDate, updatedAt, updatedByUserName, tracingStatus, tags, source, season). The order object is populated on the list endpoint and is null on GET /order-lines/:lineId.
Fetch a single line
curl "$BASE_URL/order-lines/<line-id>" -H "companyapikey: $API_KEY"
Returns the line (with full audit fields), or 404 when the line does not exist, belongs to another company, or its parent order has been deleted.
End-to-end example
# 1. Create the order together with its two lines — one atomic request
ORDER_ID=$(curl -s -X POST "$BASE_URL/orders" \
-H "companyapikey: $API_KEY" -H "Content-Type: application/json" \
-d '{
"orderNumber": "PO-2026-0042",
"buyerCompanyId": "<buyer-company-id>",
"supplierCompanyId": "<supplier-company-id>",
"lines": [
{"styleId": "<style-id-1>", "quantity": 1500, "unit": "PIECES"},
{"styleId": "<style-id-2>", "quantity": 800, "unit": "PIECES"}
]
}' | jq -r '.data.id')
# 2. Read the order's current lines (you need their ids to keep them on the next PATCH)
LINES=$(curl -s "$BASE_URL/order-lines?orderId=$ORDER_ID&limit=100" \
-H "companyapikey: $API_KEY" | jq -c '[.data[] | {id}]')
# 3. Add a third line while keeping the existing two (full-sync PATCH)
curl -s -X PATCH "$BASE_URL/orders/$ORDER_ID" \
-H "companyapikey: $API_KEY" -H "Content-Type: application/json" \
-d "{\"lines\": $(echo "$LINES" | jq -c '. + [{"styleId": "<style-id-3>", "quantity": 250, "unit": "PIECES"}]')}"
# 4. List the order's lines again
curl -s "$BASE_URL/order-lines?orderId=$ORDER_ID" -H "companyapikey: $API_KEY"
Common errors
| Status | When | How to fix |
|---|---|---|
400 |
Validation failure: missing required field, unknown field, invalid enum value, malformed date (must be YYYY-MM-DD), non-positive quantity, or string too long. |
Check the field tables above; send only documented fields with valid values. |
400 |
orderNumber already exists in your company for the same order type (code order_number_already_existing) — on create, or on PATCH when changing orderNumber or when a buyerCompanyId/supplierCompanyId change re-derives the order type into a colliding group. |
Use a different orderNumber. Deleting an order frees its number; archived orders still block reuse. |
400 |
Your company is neither the buyer nor the supplier of the order (code order_owner_not_party), or you sent both internalResponsiblePerson and internalResponsiblePersons, or only one half of the receiver pair. |
Make your company a party to the order; send responsible persons via one field only; send both receiverCompanyId and receiverFacilityProcess or neither. |
401 |
Missing or invalid companyapikey header. |
Check the header name (all lowercase) and the key value. |
404 |
The order, line or style does not exist, belongs to another company, or the order has been deleted. Also returned (code responsible_person_not_found) when an entry in internalResponsiblePersons (or the deprecated internalResponsiblePerson) matches no user in your company, and (code invalid_company_id) when a buyerCompanyId/supplierCompanyId/receiverCompanyId/vendorCompanyId/factory is not a company you can reach. On a PATCH lines entry, a referenced line id that isn't on the order returns 404 (code order_line_not_found). |
Verify the ID; remember deleted orders (and their lines) are permanently removed. |
409 |
orderLineNumber already exists on the same order (code order_not_unique) — when nested lines on create/PATCH carry a duplicate, or a create entry collides with an existing line. |
Use a different orderLineNumber, or omit it to let the server auto-number the line. |