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:

  1. Orders (/api/v2/orders) - the commercial document: order number, buyer, supplier, dates, status, and its line items.
  2. Order lines (/api/v2/order-lines) - the line items on an order (which style, how much, in what unit), exposed read-only (GET only).

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.

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",
    "tags": ["priority"],
    "buyerCompanyId": "<buyer-company-id>",
    "supplierCompanyId": "<supplier-company-id>",
    "receiverCompanyId": "<receiver-company-id>",
    "vendorCompanyId": "<vendor-company-id>",
    "factory": "<factory-company-id>",
    "buyerFacilityProcess": "QUALITY_ASSURANCE",
    "supplierFacilityProcess": "WEAVING_MILL",
    "receiverFacilityProcess": "LAUNDRY",
    "orderDate": "2026-07-01",
    "deliveryDate": "2026-09-15",
    "agreedDeliveryDate": "2026-09-10",
    "expectedShippingDate": "2026-08-28",
    "shippingDate": "2026-08-30",
    "handoverDate": "2026-09-15",
    "shipmentReference": "SHIP-2026-0042",
    "internalResponsiblePersons": ["<user-id-or-email>"],
    "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.

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. Stored as one comma-joined string: each tag is trimmed, empty tags are dropped, and a tag that itself contains a comma comes back as two tags - avoid commas inside a tag. Not nullable; clear all tags with "tags": [].
receiverCompanyId / vendorCompanyId / factory string no Additional party company IDs (max 36 chars each).
buyerFacilityProcess / supplierFacilityProcess / receiverFacilityProcess string no Facility process IDs (e.g. CUT_MAKE_TRIM) from GET /facility-processes - not display names. An unknown ID returns 400 (invalid_facility_process_ids).
orderDate, deliveryDate, agreedDeliveryDate, expectedShippingDate, shippingDate, handoverDate, actualDeliveryDate date no ISO calendar date, YYYY-MM-DD.
expectedDeliveryDate date no Deprecated - use handoverDate. Auto-mapped to handoverDate on write; when both are sent, handoverDate wins and this field is ignored (never an error). Always mirrored back in the response.
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. Emails match case-insensitively, duplicates are removed, and empty strings are skipped. The user must have your company as their primary company. 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.
counterpartyOrderNumberRef string no Your counterparty's own order number reference.

orderType is 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 (201):

{
  "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"],
    "buyerCompanyId": "<buyer-company-id>",
    "supplierCompanyId": "<supplier-company-id>",
    "receiverCompanyId": "<receiver-company-id>",
    "vendorCompanyId": "<vendor-company-id>",
    "factory": "<factory-company-id>",
    "buyerFacilityProcess": "QUALITY_ASSURANCE",
    "supplierFacilityProcess": "WEAVING_MILL",
    "receiverFacilityProcess": "LAUNDRY",
    "orderDate": "2026-07-01",
    "deliveryDate": "2026-09-15",
    "agreedDeliveryDate": "2026-09-10",
    "expectedShippingDate": "2026-08-28",
    "shippingDate": "2026-08-30",
    "handoverDate": "2026-09-15",
    "expectedDeliveryDate": "2026-09-15",
    "actualDeliveryDate": null,
    "shipmentReference": "SHIP-2026-0042",
    "internalResponsiblePersons": ["<user-id>"],
    "internalResponsiblePerson": "<user-id>",
    "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.)

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) name the process each party performs. They take a facility process ID from Retraced's shared list - CUT_MAKE_TRIM, WEAVING_MILL, QUALITY_ASSURANCE, … - not a free-text label:

# List the valid IDs (page to metadata.pagination.totalPages - there are more than one page of them)
curl "$BASE_URL/facility-processes?page=1&limit=100" -H "companyapikey: $API_KEY"

An unrecognised value is rejected with 400 (code invalid_facility_process_ids), and the error message names the offending IDs. See the Reference data section for the other ID lookup endpoints.

⚠️ Supply chain responses (GET /supply-chains/{id}) expose facility process display names such as "Cut make trim (CMT)". Those are not valid values here - always write the ID.

Which company is which is your convention, not a rule the platform enforces: Retraced only checks that your company is the buyer or supplier, that the receiver pair is supplied together (see Create an order), and that each facility process ID exists.

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:

Tracing side effects

Once an order line has been sent to Retraced's tracing module (its tracingReflectionOrderId is set), order writes carry over to the tracing request created from that line:

Visibility & scoping

One rule decides which orders your API key can see, and it surprises integrators:

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. Matches a whole tag, case-insensitively - not a substring. One value only.
orderNumber string - Filter by order number (exact match, case- and whitespace-sensitive).
isArchived true | false - Filter archived / non-archived orders. Omit to get both.
updatedAfterUnixMs / updatedBeforeUnixMs number - Unix timestamps in milliseconds; useful for incremental syncs. Both bounds are inclusive, so an order modified exactly on your cursor is returned again - de-duplicate by id on your side.

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"

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:

Leaving something out means two different things depending on the level:

So once you send lines at all, it must be the complete set you want the order to end up with. The safe recipe: read the current lines with GET /order-lines?orderId=..., then add, change or drop entries in that list and send the whole list back.

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

Reconciliation details worth knowing:

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 A style owned by your company, addressed by either its Retraced ID or the externalId you gave it via the Products API, else 404. Required when creating (no id). Responses always return the Retraced ID.
quantity number on create Positive and below 1e17. Stored with 2 decimal places - more precision is rounded half-up (10.00510.01), so read the value back rather than assuming it round-trips. 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 derived from secondaryQuantity, never set directly. Setting a quantity sets the unit to KILOGRAM; clearing the quantity clears the unit. KILOGRAM and null are still accepted for backwards compatibility, but sending secondaryUnit without secondaryQuantity in the same line entry has no effect. To repair a line whose unit and quantity disagree, send its secondaryQuantity again.

lineNo is assigned by the server (1, 2, 3, … per order) and never accepted as input. Within one request, new lines are numbered after the highest lineNo among the lines that request keeps - so if a PATCH removes the order's highest-numbered line and adds another, the freed lineNo (and any auto-generated orderLineNumber built from it) is reused. Treat lineNo as a display position, not a stable identifier: use the line id for that. An orderLineNumber that already exists on the order - or that appears twice in one request - returns 409 with code order_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

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). The order object is populated on both the list endpoint and GET /order-lines/:lineId, so a single line read already tells you which order it belongs to.

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.

Orders and order lines both carry the same 16 audit fields - createdBy and updatedBy in each of these variants: …UserId, …UserName, …CompanyId, …CompanyName, …ImpersonateId, …ImpersonateName, …ApiKeyId, …ApiKeyLabel. Writes made with an API key populate the ApiKey* pair and leave the User* pair null; writes made in the platform UI do the opposite.


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.
400 A buyerFacilityProcess / supplierFacilityProcess / receiverFacilityProcess value is not a known facility process ID (code invalid_facility_process_ids). The message lists the offending IDs. Send an ID from GET /api/v2/facility-processes - these fields take IDs (CUT_MAKE_TRIM), not display names ("Cut make trim (CMT)").
401 Missing or invalid credentials (code token_or_api_key_invalid). Besides companyapikey, the endpoints also accept a platform bearer token. Check the header name (all lowercase) and the key value.
403 Authenticated, but the credential does not resolve to a company (code insufficient_rights). Use a company API key; a key that isn't bound to a company can't read or write orders.
404 A styleId on a nested line is unknown, archived, or not owned by your company (code style_not_found). Create or unarchive the style first, and check it belongs to your company.
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. Swapping two numbers needs two requests (see Update an order's lines).

Two behaviours that are not errors, and bite silently: