Skip to content

List an order's line items

GET
/orders/{orderNumber}/items
curl --request GET \
--url 'https://api.partners.collaterate.com/v1/orders/1/items?limit=50' \
--header 'Authorization: Bearer <token>'

The line items of one order, keyset-paginated: SKU, quantity, per-line money, and per-line ship state.

Cancelled lines stay in the list

A cancelled line is returned with cancelled: true, not filtered out. A line that vanished between two polls would look like a bug in your reconciliation; a flag is an answer. Decide what to do with cancelled lines in your own code – do not assume the list contains only live ones.

Lines you will not see

Two exclusions are applied and neither is a filter you can turn off:

  • lines the platform hides from the customer who placed the order are hidden from you too (633 of 664,132 lines over twelve months).
  • internal production records – rework and proof records, which are children of a real line and never carry a SKU – are excluded. Including them would double-count an order’s contents.

An order can therefore legitimately return an empty items array. That is a 200, not a 404; a 404 means the order number does not resolve for you at all.

There is also one rare data condition worth knowing about if you reconcile totals: a line whose own site assignment disagrees with its order’s is shown to nobody, because neither site’s claim on it can be trusted. It affected 26 of 664,132 lines over twelve months. If an order’s line totals do not sum to its totals.item, this is the first thing to ask Partner Integrations about.

orderNumber
required
integer
cursor
string

Opaque pagination token from a previous response’s nextCursor. Endpoint-specific: a cursor from another endpoint is rejected as 400 invalid_cursor.

limit
integer
default: 50 >= 1 <= 200

Page size. Defaults to 50. Values above 200 are capped at 200, not rejected.

A page of line items.

Media typeapplication/json
object
items
required
Array<object>

One line item of an order. There is no line id: a line is identified by (orderNumber, itemNumber).

object
orderNumber
required
integer
siteId
required
integer
itemNumber
required

The platform’s job number for this line, and the identifier to quote when discussing it. In practice always present on a line belonging to an order; typed as nullable because the underlying column is.

integer | null
sku
required

The SKU, if the line resolves to one. Frequently null – 55% of production line items have no SKU, because a configured print job is not necessarily a catalogue product. If you place orders by SKU you will read your own SKU back here; if you read lines you did not create, handle null.

string | null
productName
required
string | null
productCode
required

The platform’s product identifier for the line. A string, not a number.

string | null
jobName
required

The customer-facing name given to this job, if any.

string | null
externalId
required

Whatever your system set when the line was created, echoed back unchanged. null if nothing was set. This is the field to correlate against your own records.

string | null
quantity
required

The quantity being produced for this line, after any adjustment. This is the only quantity the API publishes; it is what will be made and shipped.

integer
lineListTotal
required

The line’s extended list amount – the whole line before discounts and adjustments. This is not a per-unit price; do not multiply it by quantity. It was published as unitPrice before 2026-07-29, which was wrong: across 2.36M multi-quantity lines it equalled quantity x unit on 0.003% of them. No per-unit price is published, because none is stored.

Decimal amount encoded as a string, not a JSON number, for the same reason as OrderTotals – the column is numeric(24,10) and a 64-bit float cannot hold it. Parse with a decimal type, never parseFloat/Number(). null means not priced, which is not the same as zero.

string | null
lineTotal
required

The line’s final extended amount, after discounts and adjustments – what this line contributes to the order. Compare with lineListTotal to see what came off. Same decimal-string rules. Note that the sum of these will not necessarily equal the order’s totals.item – see the operation description on excluded lines.

string | null
shipped
required

Whether this line has shipped. A line-level flag: an order can be partly shipped, with some lines true and others false.

boolean
shippedOn
required
string | null format: date-time
cancelled
required

true for a cancelled line, which stays in the list rather than disappearing. Do not assume the list contains only live lines.

boolean
estimatedShipBy
required

Current internal estimate for this line. An estimate, not a commitment.

string | null format: date-time
shipBy
required
string | null format: date-time
turnaroundBusinessDays
required

Production turnaround for this line in business days, if one is set.

integer | null
nextCursor
required

Opaque cursor for the next page, or null when this is the last page. Terminate your loop on null and on nothing else.

string | null
Examples
Exampledefault
{
"items": [
{
"orderNumber": 1000234,
"siteId": 7,
"itemNumber": 884213,
"sku": "BC-16PT-MATTE-1000",
"productName": "Business Cards",
"productCode": "business-cards",
"jobName": "Q3 sales team cards",
"externalId": "acme-line-4471",
"quantity": 1000,
"lineListTotal": "49.0000000000",
"lineTotal": "46.5500000000",
"shipped": true,
"shippedOn": "2026-06-03T09:10:00.000Z",
"cancelled": false,
"estimatedShipBy": "2026-06-03T00:00:00.000Z",
"shipBy": "2026-06-04T00:00:00.000Z",
"turnaroundBusinessDays": 3
}
],
"nextCursor": null
}

The order number in the path is not a positive integer, or cursor could not be decoded.

Media typeapplication/problem+json

RFC 9457 application/problem+json body. code is the published, stable, machine-readable field to branch your integration logic on – detail is a human-readable string that may be reworded over time and must not be parsed.

This table is the whole published set: every code this API can return appears below, and nothing below is unreachable. test/docs/openapi-matches-reality.test.ts compares the enum to the codes the handlers actually construct, so a code added to one side and not the other fails the build rather than shipping.

Published code values:

code HTTP status Meaning Retry?
invalid_cursor 400 The cursor query parameter could not be decoded. No – restart pagination with no cursor. Do not resend the same value.
invalid_site_id 400 The siteId filter is not a positive integer, or it names a site outside your grant. detail says which. No – fix the parameter against GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. No – fix the path.
invalid_product_id 400 The productId path segment on GET /v1/products/{productId} does not match the SLO_/SLS_-prefixed shape. No – fix the path.
invalid_service_type 400 GET /v1/products’s serviceType filter is not one of the values this API can order (STOCK or POD). No – fix the parameter.
updated_since_not_supported 400 An updatedSince parameter was sent. Rejected rather than ignored, on purpose – see “No updatedSince / no change polling” above. No – remove the parameter; there is no change-polling mechanism to switch to.
invalid_request 400 POST /v1/orders’s request body failed validation – malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness or a misplaced shipToor GET /v1/orders/submissions’s status/limit filter is malformed. detail names the exact violation. No – fix the request.
invalid_submission_id 400 The submissionId path segment on GET /v1/orders/submissions/{submissionId} is not a well-formed UUID. No – fix the path.
invalid_quantity 400 The quantity on POST /v1/products/{productId}/quote is missing or not a positive integer. No – fix the request body.
invalid_token 401 The credential is missing, expired, or invalid. No – mint a new token, retry once, then investigate the credential.
insufficient_scope 403 The token lacks the scope required for this operation. No – mint a token with the required scope.
order_not_found 404 No such order, or one that exists outside your granted sites (see above). No.
product_not_found 404 No such product, or one that exists but belongs to a site outside your granted sites (same rule as order_not_found). No.
quote_product_not_found 404 The product resolved in our own catalog but Collaterate could not price it. Rare. No.
submission_not_found 404 No submission exists with this id for your credential – whether it never existed or belongs to another partner, identically. No.
ordering_not_provisioned 409 POST /v1/orders’s siteId has no ordering user configured. This is set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. No – ask Partner Integrations to provision an ordering user for the site, then retry.
partner_order_id_reused 409 POST /v1/orders’s partnerOrderId was already used, with a request body that does not match this one. Resending the identical body under the same partnerOrderId is safe and returns the original submission (see the operation description); reusing it for a materially different order is rejected instead of silently creating a second order. No – use a new partnerOrderId, or resend the exact original body to poll the existing submission.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own validation message. No – fix the request body (e.g. reduce quantity).
rate_limit_exceeded 429 Rate limit or daily quota exceeded. Yes – honor Retry-After.
internal_error 500 Unexpected server-side failure. Detail is always the fixed string below, never the underlying error. No, or with caution – if persistent, contact Partner Integrations.
service_unavailable 503 Transient failure – typically the database path, but also a Lambda that crashed, timed out, or hit its own concurrency limit before any handler ran. Yes, with backoff.

Three statuses are answered by the API gateway before your request reaches application code: 401 (authorizer denial), 429 (usage-plan throttle), and 503 (the Lambda integration itself failed). All three are configured to return this same document with the codes above, so one parser and one branch on code covers every error this API produces. The 403 on GET /v1/ping is the sole exception anywhere in the API – it comes from the edge firewall, which is not ours to shape, and carries no problem document.

object
type
required

Always the literal string about:blank today; reserved by RFC 9457 for future use.

string
Allowed value: about:blank
title
required

Short, human-readable summary of the HTTP status (e.g. “Not Found”).

string
status
required

The HTTP status code, repeated in the body for convenience.

integer
code
required

Stable, machine-readable error identifier. Safe to branch on. See the table above.

string
Allowed values: invalid_cursor invalid_site_id invalid_order_number invalid_product_id invalid_quantity invalid_service_type updated_since_not_supported invalid_request invalid_submission_id invalid_token insufficient_scope order_not_found product_not_found quote_product_not_found submission_not_found ordering_not_provisioned partner_order_id_reused quote_invalid rate_limit_exceeded internal_error service_unavailable
detail
required

Human-readable explanation. Do not parse this – it may be reworded without notice.

string
requestId
required

Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.

string
Example
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "invalid_order_number",
"detail": "\"orderNumber\" must be a positive integer, got: 10-0234",
"requestId": "8f3c1e2a-..."
}

Missing, expired, or otherwise invalid credential. Not retryable as-is.

Produced by the API gateway’s authorizer, before the request reaches any application code, so every cause – no Authorization header, a malformed one, a token that is expired, wrongly signed, from the wrong pool, or belongs to a credential that has been disabled – yields this identical response. The gateway is configured to answer in the same application/problem+json shape as everything else, so you do not need a second parser for this status; what it cannot do is tell you which of those causes applied.

Media typeapplication/problem+json

RFC 9457 application/problem+json body. code is the published, stable, machine-readable field to branch your integration logic on – detail is a human-readable string that may be reworded over time and must not be parsed.

This table is the whole published set: every code this API can return appears below, and nothing below is unreachable. test/docs/openapi-matches-reality.test.ts compares the enum to the codes the handlers actually construct, so a code added to one side and not the other fails the build rather than shipping.

Published code values:

code HTTP status Meaning Retry?
invalid_cursor 400 The cursor query parameter could not be decoded. No – restart pagination with no cursor. Do not resend the same value.
invalid_site_id 400 The siteId filter is not a positive integer, or it names a site outside your grant. detail says which. No – fix the parameter against GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. No – fix the path.
invalid_product_id 400 The productId path segment on GET /v1/products/{productId} does not match the SLO_/SLS_-prefixed shape. No – fix the path.
invalid_service_type 400 GET /v1/products’s serviceType filter is not one of the values this API can order (STOCK or POD). No – fix the parameter.
updated_since_not_supported 400 An updatedSince parameter was sent. Rejected rather than ignored, on purpose – see “No updatedSince / no change polling” above. No – remove the parameter; there is no change-polling mechanism to switch to.
invalid_request 400 POST /v1/orders’s request body failed validation – malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness or a misplaced shipToor GET /v1/orders/submissions’s status/limit filter is malformed. detail names the exact violation. No – fix the request.
invalid_submission_id 400 The submissionId path segment on GET /v1/orders/submissions/{submissionId} is not a well-formed UUID. No – fix the path.
invalid_quantity 400 The quantity on POST /v1/products/{productId}/quote is missing or not a positive integer. No – fix the request body.
invalid_token 401 The credential is missing, expired, or invalid. No – mint a new token, retry once, then investigate the credential.
insufficient_scope 403 The token lacks the scope required for this operation. No – mint a token with the required scope.
order_not_found 404 No such order, or one that exists outside your granted sites (see above). No.
product_not_found 404 No such product, or one that exists but belongs to a site outside your granted sites (same rule as order_not_found). No.
quote_product_not_found 404 The product resolved in our own catalog but Collaterate could not price it. Rare. No.
submission_not_found 404 No submission exists with this id for your credential – whether it never existed or belongs to another partner, identically. No.
ordering_not_provisioned 409 POST /v1/orders’s siteId has no ordering user configured. This is set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. No – ask Partner Integrations to provision an ordering user for the site, then retry.
partner_order_id_reused 409 POST /v1/orders’s partnerOrderId was already used, with a request body that does not match this one. Resending the identical body under the same partnerOrderId is safe and returns the original submission (see the operation description); reusing it for a materially different order is rejected instead of silently creating a second order. No – use a new partnerOrderId, or resend the exact original body to poll the existing submission.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own validation message. No – fix the request body (e.g. reduce quantity).
rate_limit_exceeded 429 Rate limit or daily quota exceeded. Yes – honor Retry-After.
internal_error 500 Unexpected server-side failure. Detail is always the fixed string below, never the underlying error. No, or with caution – if persistent, contact Partner Integrations.
service_unavailable 503 Transient failure – typically the database path, but also a Lambda that crashed, timed out, or hit its own concurrency limit before any handler ran. Yes, with backoff.

Three statuses are answered by the API gateway before your request reaches application code: 401 (authorizer denial), 429 (usage-plan throttle), and 503 (the Lambda integration itself failed). All three are configured to return this same document with the codes above, so one parser and one branch on code covers every error this API produces. The 403 on GET /v1/ping is the sole exception anywhere in the API – it comes from the edge firewall, which is not ours to shape, and carries no problem document.

object
type
required

Always the literal string about:blank today; reserved by RFC 9457 for future use.

string
Allowed value: about:blank
title
required

Short, human-readable summary of the HTTP status (e.g. “Not Found”).

string
status
required

The HTTP status code, repeated in the body for convenience.

integer
code
required

Stable, machine-readable error identifier. Safe to branch on. See the table above.

string
Allowed values: invalid_cursor invalid_site_id invalid_order_number invalid_product_id invalid_quantity invalid_service_type updated_since_not_supported invalid_request invalid_submission_id invalid_token insufficient_scope order_not_found product_not_found quote_product_not_found submission_not_found ordering_not_provisioned partner_order_id_reused quote_invalid rate_limit_exceeded internal_error service_unavailable
detail
required

Human-readable explanation. Do not parse this – it may be reworded without notice.

string
requestId
required

Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.

string
Example
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"code": "invalid_token",
"detail": "The credential presented with this request is missing, expired, or invalid.",
"requestId": "8f3c1e2a-..."
}

The credential is valid but lacks the scope required for this operation (for example, a token minted without partner-api/orders:read). Not retryable as-is – request a token with the required scope. Not to be confused with an out-of-scope order, which is a 404 (see above), or with the edge firewall’s 403 on /ping, which carries no problem document at all.

Media typeapplication/problem+json

RFC 9457 application/problem+json body. code is the published, stable, machine-readable field to branch your integration logic on – detail is a human-readable string that may be reworded over time and must not be parsed.

This table is the whole published set: every code this API can return appears below, and nothing below is unreachable. test/docs/openapi-matches-reality.test.ts compares the enum to the codes the handlers actually construct, so a code added to one side and not the other fails the build rather than shipping.

Published code values:

code HTTP status Meaning Retry?
invalid_cursor 400 The cursor query parameter could not be decoded. No – restart pagination with no cursor. Do not resend the same value.
invalid_site_id 400 The siteId filter is not a positive integer, or it names a site outside your grant. detail says which. No – fix the parameter against GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. No – fix the path.
invalid_product_id 400 The productId path segment on GET /v1/products/{productId} does not match the SLO_/SLS_-prefixed shape. No – fix the path.
invalid_service_type 400 GET /v1/products’s serviceType filter is not one of the values this API can order (STOCK or POD). No – fix the parameter.
updated_since_not_supported 400 An updatedSince parameter was sent. Rejected rather than ignored, on purpose – see “No updatedSince / no change polling” above. No – remove the parameter; there is no change-polling mechanism to switch to.
invalid_request 400 POST /v1/orders’s request body failed validation – malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness or a misplaced shipToor GET /v1/orders/submissions’s status/limit filter is malformed. detail names the exact violation. No – fix the request.
invalid_submission_id 400 The submissionId path segment on GET /v1/orders/submissions/{submissionId} is not a well-formed UUID. No – fix the path.
invalid_quantity 400 The quantity on POST /v1/products/{productId}/quote is missing or not a positive integer. No – fix the request body.
invalid_token 401 The credential is missing, expired, or invalid. No – mint a new token, retry once, then investigate the credential.
insufficient_scope 403 The token lacks the scope required for this operation. No – mint a token with the required scope.
order_not_found 404 No such order, or one that exists outside your granted sites (see above). No.
product_not_found 404 No such product, or one that exists but belongs to a site outside your granted sites (same rule as order_not_found). No.
quote_product_not_found 404 The product resolved in our own catalog but Collaterate could not price it. Rare. No.
submission_not_found 404 No submission exists with this id for your credential – whether it never existed or belongs to another partner, identically. No.
ordering_not_provisioned 409 POST /v1/orders’s siteId has no ordering user configured. This is set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. No – ask Partner Integrations to provision an ordering user for the site, then retry.
partner_order_id_reused 409 POST /v1/orders’s partnerOrderId was already used, with a request body that does not match this one. Resending the identical body under the same partnerOrderId is safe and returns the original submission (see the operation description); reusing it for a materially different order is rejected instead of silently creating a second order. No – use a new partnerOrderId, or resend the exact original body to poll the existing submission.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own validation message. No – fix the request body (e.g. reduce quantity).
rate_limit_exceeded 429 Rate limit or daily quota exceeded. Yes – honor Retry-After.
internal_error 500 Unexpected server-side failure. Detail is always the fixed string below, never the underlying error. No, or with caution – if persistent, contact Partner Integrations.
service_unavailable 503 Transient failure – typically the database path, but also a Lambda that crashed, timed out, or hit its own concurrency limit before any handler ran. Yes, with backoff.

Three statuses are answered by the API gateway before your request reaches application code: 401 (authorizer denial), 429 (usage-plan throttle), and 503 (the Lambda integration itself failed). All three are configured to return this same document with the codes above, so one parser and one branch on code covers every error this API produces. The 403 on GET /v1/ping is the sole exception anywhere in the API – it comes from the edge firewall, which is not ours to shape, and carries no problem document.

object
type
required

Always the literal string about:blank today; reserved by RFC 9457 for future use.

string
Allowed value: about:blank
title
required

Short, human-readable summary of the HTTP status (e.g. “Not Found”).

string
status
required

The HTTP status code, repeated in the body for convenience.

integer
code
required

Stable, machine-readable error identifier. Safe to branch on. See the table above.

string
Allowed values: invalid_cursor invalid_site_id invalid_order_number invalid_product_id invalid_quantity invalid_service_type updated_since_not_supported invalid_request invalid_submission_id invalid_token insufficient_scope order_not_found product_not_found quote_product_not_found submission_not_found ordering_not_provisioned partner_order_id_reused quote_invalid rate_limit_exceeded internal_error service_unavailable
detail
required

Human-readable explanation. Do not parse this – it may be reworded without notice.

string
requestId
required

Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.

string
Example
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"code": "insufficient_scope",
"detail": "This request requires the \"partner-api/orders:read\" scope.",
"requestId": "8f3c1e2a-..."
}

No such order, OR an order that exists but is outside your granted sites. These two cases are indistinguishable on purpose. Not retryable as-is.

On the shipments and items sub-resources this status is about the ORDER, never the collection: an order that resolves for you but has no shipments (or no visible items) yet returns 200 with an empty array. A 404 from those endpoints means the order number itself does not resolve for you.

Media typeapplication/problem+json

RFC 9457 application/problem+json body. code is the published, stable, machine-readable field to branch your integration logic on – detail is a human-readable string that may be reworded over time and must not be parsed.

This table is the whole published set: every code this API can return appears below, and nothing below is unreachable. test/docs/openapi-matches-reality.test.ts compares the enum to the codes the handlers actually construct, so a code added to one side and not the other fails the build rather than shipping.

Published code values:

code HTTP status Meaning Retry?
invalid_cursor 400 The cursor query parameter could not be decoded. No – restart pagination with no cursor. Do not resend the same value.
invalid_site_id 400 The siteId filter is not a positive integer, or it names a site outside your grant. detail says which. No – fix the parameter against GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. No – fix the path.
invalid_product_id 400 The productId path segment on GET /v1/products/{productId} does not match the SLO_/SLS_-prefixed shape. No – fix the path.
invalid_service_type 400 GET /v1/products’s serviceType filter is not one of the values this API can order (STOCK or POD). No – fix the parameter.
updated_since_not_supported 400 An updatedSince parameter was sent. Rejected rather than ignored, on purpose – see “No updatedSince / no change polling” above. No – remove the parameter; there is no change-polling mechanism to switch to.
invalid_request 400 POST /v1/orders’s request body failed validation – malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness or a misplaced shipToor GET /v1/orders/submissions’s status/limit filter is malformed. detail names the exact violation. No – fix the request.
invalid_submission_id 400 The submissionId path segment on GET /v1/orders/submissions/{submissionId} is not a well-formed UUID. No – fix the path.
invalid_quantity 400 The quantity on POST /v1/products/{productId}/quote is missing or not a positive integer. No – fix the request body.
invalid_token 401 The credential is missing, expired, or invalid. No – mint a new token, retry once, then investigate the credential.
insufficient_scope 403 The token lacks the scope required for this operation. No – mint a token with the required scope.
order_not_found 404 No such order, or one that exists outside your granted sites (see above). No.
product_not_found 404 No such product, or one that exists but belongs to a site outside your granted sites (same rule as order_not_found). No.
quote_product_not_found 404 The product resolved in our own catalog but Collaterate could not price it. Rare. No.
submission_not_found 404 No submission exists with this id for your credential – whether it never existed or belongs to another partner, identically. No.
ordering_not_provisioned 409 POST /v1/orders’s siteId has no ordering user configured. This is set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. No – ask Partner Integrations to provision an ordering user for the site, then retry.
partner_order_id_reused 409 POST /v1/orders’s partnerOrderId was already used, with a request body that does not match this one. Resending the identical body under the same partnerOrderId is safe and returns the original submission (see the operation description); reusing it for a materially different order is rejected instead of silently creating a second order. No – use a new partnerOrderId, or resend the exact original body to poll the existing submission.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own validation message. No – fix the request body (e.g. reduce quantity).
rate_limit_exceeded 429 Rate limit or daily quota exceeded. Yes – honor Retry-After.
internal_error 500 Unexpected server-side failure. Detail is always the fixed string below, never the underlying error. No, or with caution – if persistent, contact Partner Integrations.
service_unavailable 503 Transient failure – typically the database path, but also a Lambda that crashed, timed out, or hit its own concurrency limit before any handler ran. Yes, with backoff.

Three statuses are answered by the API gateway before your request reaches application code: 401 (authorizer denial), 429 (usage-plan throttle), and 503 (the Lambda integration itself failed). All three are configured to return this same document with the codes above, so one parser and one branch on code covers every error this API produces. The 403 on GET /v1/ping is the sole exception anywhere in the API – it comes from the edge firewall, which is not ours to shape, and carries no problem document.

object
type
required

Always the literal string about:blank today; reserved by RFC 9457 for future use.

string
Allowed value: about:blank
title
required

Short, human-readable summary of the HTTP status (e.g. “Not Found”).

string
status
required

The HTTP status code, repeated in the body for convenience.

integer
code
required

Stable, machine-readable error identifier. Safe to branch on. See the table above.

string
Allowed values: invalid_cursor invalid_site_id invalid_order_number invalid_product_id invalid_quantity invalid_service_type updated_since_not_supported invalid_request invalid_submission_id invalid_token insufficient_scope order_not_found product_not_found quote_product_not_found submission_not_found ordering_not_provisioned partner_order_id_reused quote_invalid rate_limit_exceeded internal_error service_unavailable
detail
required

Human-readable explanation. Do not parse this – it may be reworded without notice.

string
requestId
required

Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.

string
Example
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"code": "order_not_found",
"detail": "No order was found matching the request.",
"requestId": "8f3c1e2a-..."
}

Your rate limit or daily quota was exceeded. Retryable – honor the Retry-After header (seconds) before your next attempt.

Like the 401, this comes from the gateway’s usage plan rather than from application code, and is configured to carry the same problem document as every other error so a single parser covers the whole API.

Media typeapplication/problem+json

RFC 9457 application/problem+json body. code is the published, stable, machine-readable field to branch your integration logic on – detail is a human-readable string that may be reworded over time and must not be parsed.

This table is the whole published set: every code this API can return appears below, and nothing below is unreachable. test/docs/openapi-matches-reality.test.ts compares the enum to the codes the handlers actually construct, so a code added to one side and not the other fails the build rather than shipping.

Published code values:

code HTTP status Meaning Retry?
invalid_cursor 400 The cursor query parameter could not be decoded. No – restart pagination with no cursor. Do not resend the same value.
invalid_site_id 400 The siteId filter is not a positive integer, or it names a site outside your grant. detail says which. No – fix the parameter against GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. No – fix the path.
invalid_product_id 400 The productId path segment on GET /v1/products/{productId} does not match the SLO_/SLS_-prefixed shape. No – fix the path.
invalid_service_type 400 GET /v1/products’s serviceType filter is not one of the values this API can order (STOCK or POD). No – fix the parameter.
updated_since_not_supported 400 An updatedSince parameter was sent. Rejected rather than ignored, on purpose – see “No updatedSince / no change polling” above. No – remove the parameter; there is no change-polling mechanism to switch to.
invalid_request 400 POST /v1/orders’s request body failed validation – malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness or a misplaced shipToor GET /v1/orders/submissions’s status/limit filter is malformed. detail names the exact violation. No – fix the request.
invalid_submission_id 400 The submissionId path segment on GET /v1/orders/submissions/{submissionId} is not a well-formed UUID. No – fix the path.
invalid_quantity 400 The quantity on POST /v1/products/{productId}/quote is missing or not a positive integer. No – fix the request body.
invalid_token 401 The credential is missing, expired, or invalid. No – mint a new token, retry once, then investigate the credential.
insufficient_scope 403 The token lacks the scope required for this operation. No – mint a token with the required scope.
order_not_found 404 No such order, or one that exists outside your granted sites (see above). No.
product_not_found 404 No such product, or one that exists but belongs to a site outside your granted sites (same rule as order_not_found). No.
quote_product_not_found 404 The product resolved in our own catalog but Collaterate could not price it. Rare. No.
submission_not_found 404 No submission exists with this id for your credential – whether it never existed or belongs to another partner, identically. No.
ordering_not_provisioned 409 POST /v1/orders’s siteId has no ordering user configured. This is set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. No – ask Partner Integrations to provision an ordering user for the site, then retry.
partner_order_id_reused 409 POST /v1/orders’s partnerOrderId was already used, with a request body that does not match this one. Resending the identical body under the same partnerOrderId is safe and returns the original submission (see the operation description); reusing it for a materially different order is rejected instead of silently creating a second order. No – use a new partnerOrderId, or resend the exact original body to poll the existing submission.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own validation message. No – fix the request body (e.g. reduce quantity).
rate_limit_exceeded 429 Rate limit or daily quota exceeded. Yes – honor Retry-After.
internal_error 500 Unexpected server-side failure. Detail is always the fixed string below, never the underlying error. No, or with caution – if persistent, contact Partner Integrations.
service_unavailable 503 Transient failure – typically the database path, but also a Lambda that crashed, timed out, or hit its own concurrency limit before any handler ran. Yes, with backoff.

Three statuses are answered by the API gateway before your request reaches application code: 401 (authorizer denial), 429 (usage-plan throttle), and 503 (the Lambda integration itself failed). All three are configured to return this same document with the codes above, so one parser and one branch on code covers every error this API produces. The 403 on GET /v1/ping is the sole exception anywhere in the API – it comes from the edge firewall, which is not ours to shape, and carries no problem document.

object
type
required

Always the literal string about:blank today; reserved by RFC 9457 for future use.

string
Allowed value: about:blank
title
required

Short, human-readable summary of the HTTP status (e.g. “Not Found”).

string
status
required

The HTTP status code, repeated in the body for convenience.

integer
code
required

Stable, machine-readable error identifier. Safe to branch on. See the table above.

string
Allowed values: invalid_cursor invalid_site_id invalid_order_number invalid_product_id invalid_quantity invalid_service_type updated_since_not_supported invalid_request invalid_submission_id invalid_token insufficient_scope order_not_found product_not_found quote_product_not_found submission_not_found ordering_not_provisioned partner_order_id_reused quote_invalid rate_limit_exceeded internal_error service_unavailable
detail
required

Human-readable explanation. Do not parse this – it may be reworded without notice.

string
requestId
required

Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.

string
Example
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"code": "rate_limit_exceeded",
"detail": "The request rate limit has been exceeded. Retry after the indicated delay.",
"requestId": "8f3c1e2a-..."
}
Retry-After
integer

Seconds to wait before retrying.

An unexpected server-side failure. detail is always the fixed string below and never the underlying error – a database message quoted back to a partner is a disclosure, so nothing is interpolated into it. The requestId resolves to the log entry that does carry the cause; send it to Partner Integrations rather than guessing.

Media typeapplication/problem+json

RFC 9457 application/problem+json body. code is the published, stable, machine-readable field to branch your integration logic on – detail is a human-readable string that may be reworded over time and must not be parsed.

This table is the whole published set: every code this API can return appears below, and nothing below is unreachable. test/docs/openapi-matches-reality.test.ts compares the enum to the codes the handlers actually construct, so a code added to one side and not the other fails the build rather than shipping.

Published code values:

code HTTP status Meaning Retry?
invalid_cursor 400 The cursor query parameter could not be decoded. No – restart pagination with no cursor. Do not resend the same value.
invalid_site_id 400 The siteId filter is not a positive integer, or it names a site outside your grant. detail says which. No – fix the parameter against GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. No – fix the path.
invalid_product_id 400 The productId path segment on GET /v1/products/{productId} does not match the SLO_/SLS_-prefixed shape. No – fix the path.
invalid_service_type 400 GET /v1/products’s serviceType filter is not one of the values this API can order (STOCK or POD). No – fix the parameter.
updated_since_not_supported 400 An updatedSince parameter was sent. Rejected rather than ignored, on purpose – see “No updatedSince / no change polling” above. No – remove the parameter; there is no change-polling mechanism to switch to.
invalid_request 400 POST /v1/orders’s request body failed validation – malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness or a misplaced shipToor GET /v1/orders/submissions’s status/limit filter is malformed. detail names the exact violation. No – fix the request.
invalid_submission_id 400 The submissionId path segment on GET /v1/orders/submissions/{submissionId} is not a well-formed UUID. No – fix the path.
invalid_quantity 400 The quantity on POST /v1/products/{productId}/quote is missing or not a positive integer. No – fix the request body.
invalid_token 401 The credential is missing, expired, or invalid. No – mint a new token, retry once, then investigate the credential.
insufficient_scope 403 The token lacks the scope required for this operation. No – mint a token with the required scope.
order_not_found 404 No such order, or one that exists outside your granted sites (see above). No.
product_not_found 404 No such product, or one that exists but belongs to a site outside your granted sites (same rule as order_not_found). No.
quote_product_not_found 404 The product resolved in our own catalog but Collaterate could not price it. Rare. No.
submission_not_found 404 No submission exists with this id for your credential – whether it never existed or belongs to another partner, identically. No.
ordering_not_provisioned 409 POST /v1/orders’s siteId has no ordering user configured. This is set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. No – ask Partner Integrations to provision an ordering user for the site, then retry.
partner_order_id_reused 409 POST /v1/orders’s partnerOrderId was already used, with a request body that does not match this one. Resending the identical body under the same partnerOrderId is safe and returns the original submission (see the operation description); reusing it for a materially different order is rejected instead of silently creating a second order. No – use a new partnerOrderId, or resend the exact original body to poll the existing submission.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own validation message. No – fix the request body (e.g. reduce quantity).
rate_limit_exceeded 429 Rate limit or daily quota exceeded. Yes – honor Retry-After.
internal_error 500 Unexpected server-side failure. Detail is always the fixed string below, never the underlying error. No, or with caution – if persistent, contact Partner Integrations.
service_unavailable 503 Transient failure – typically the database path, but also a Lambda that crashed, timed out, or hit its own concurrency limit before any handler ran. Yes, with backoff.

Three statuses are answered by the API gateway before your request reaches application code: 401 (authorizer denial), 429 (usage-plan throttle), and 503 (the Lambda integration itself failed). All three are configured to return this same document with the codes above, so one parser and one branch on code covers every error this API produces. The 403 on GET /v1/ping is the sole exception anywhere in the API – it comes from the edge firewall, which is not ours to shape, and carries no problem document.

object
type
required

Always the literal string about:blank today; reserved by RFC 9457 for future use.

string
Allowed value: about:blank
title
required

Short, human-readable summary of the HTTP status (e.g. “Not Found”).

string
status
required

The HTTP status code, repeated in the body for convenience.

integer
code
required

Stable, machine-readable error identifier. Safe to branch on. See the table above.

string
Allowed values: invalid_cursor invalid_site_id invalid_order_number invalid_product_id invalid_quantity invalid_service_type updated_since_not_supported invalid_request invalid_submission_id invalid_token insufficient_scope order_not_found product_not_found quote_product_not_found submission_not_found ordering_not_provisioned partner_order_id_reused quote_invalid rate_limit_exceeded internal_error service_unavailable
detail
required

Human-readable explanation. Do not parse this – it may be reworded without notice.

string
requestId
required

Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.

string
Example
{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"code": "internal_error",
"detail": "An unexpected error occurred.",
"requestId": "8f3c1e2a-..."
}

Transient failure in the service or the database it reads from (for example, the Aurora reader could not be reached in time), or the request never reached application code at all because the Lambda behind it crashed, timed out, or hit its own concurrency limit. Retryable, with backoff.

Media typeapplication/problem+json

RFC 9457 application/problem+json body. code is the published, stable, machine-readable field to branch your integration logic on – detail is a human-readable string that may be reworded over time and must not be parsed.

This table is the whole published set: every code this API can return appears below, and nothing below is unreachable. test/docs/openapi-matches-reality.test.ts compares the enum to the codes the handlers actually construct, so a code added to one side and not the other fails the build rather than shipping.

Published code values:

code HTTP status Meaning Retry?
invalid_cursor 400 The cursor query parameter could not be decoded. No – restart pagination with no cursor. Do not resend the same value.
invalid_site_id 400 The siteId filter is not a positive integer, or it names a site outside your grant. detail says which. No – fix the parameter against GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. No – fix the path.
invalid_product_id 400 The productId path segment on GET /v1/products/{productId} does not match the SLO_/SLS_-prefixed shape. No – fix the path.
invalid_service_type 400 GET /v1/products’s serviceType filter is not one of the values this API can order (STOCK or POD). No – fix the parameter.
updated_since_not_supported 400 An updatedSince parameter was sent. Rejected rather than ignored, on purpose – see “No updatedSince / no change polling” above. No – remove the parameter; there is no change-polling mechanism to switch to.
invalid_request 400 POST /v1/orders’s request body failed validation – malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness or a misplaced shipToor GET /v1/orders/submissions’s status/limit filter is malformed. detail names the exact violation. No – fix the request.
invalid_submission_id 400 The submissionId path segment on GET /v1/orders/submissions/{submissionId} is not a well-formed UUID. No – fix the path.
invalid_quantity 400 The quantity on POST /v1/products/{productId}/quote is missing or not a positive integer. No – fix the request body.
invalid_token 401 The credential is missing, expired, or invalid. No – mint a new token, retry once, then investigate the credential.
insufficient_scope 403 The token lacks the scope required for this operation. No – mint a token with the required scope.
order_not_found 404 No such order, or one that exists outside your granted sites (see above). No.
product_not_found 404 No such product, or one that exists but belongs to a site outside your granted sites (same rule as order_not_found). No.
quote_product_not_found 404 The product resolved in our own catalog but Collaterate could not price it. Rare. No.
submission_not_found 404 No submission exists with this id for your credential – whether it never existed or belongs to another partner, identically. No.
ordering_not_provisioned 409 POST /v1/orders’s siteId has no ordering user configured. This is set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. No – ask Partner Integrations to provision an ordering user for the site, then retry.
partner_order_id_reused 409 POST /v1/orders’s partnerOrderId was already used, with a request body that does not match this one. Resending the identical body under the same partnerOrderId is safe and returns the original submission (see the operation description); reusing it for a materially different order is rejected instead of silently creating a second order. No – use a new partnerOrderId, or resend the exact original body to poll the existing submission.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own validation message. No – fix the request body (e.g. reduce quantity).
rate_limit_exceeded 429 Rate limit or daily quota exceeded. Yes – honor Retry-After.
internal_error 500 Unexpected server-side failure. Detail is always the fixed string below, never the underlying error. No, or with caution – if persistent, contact Partner Integrations.
service_unavailable 503 Transient failure – typically the database path, but also a Lambda that crashed, timed out, or hit its own concurrency limit before any handler ran. Yes, with backoff.

Three statuses are answered by the API gateway before your request reaches application code: 401 (authorizer denial), 429 (usage-plan throttle), and 503 (the Lambda integration itself failed). All three are configured to return this same document with the codes above, so one parser and one branch on code covers every error this API produces. The 403 on GET /v1/ping is the sole exception anywhere in the API – it comes from the edge firewall, which is not ours to shape, and carries no problem document.

object
type
required

Always the literal string about:blank today; reserved by RFC 9457 for future use.

string
Allowed value: about:blank
title
required

Short, human-readable summary of the HTTP status (e.g. “Not Found”).

string
status
required

The HTTP status code, repeated in the body for convenience.

integer
code
required

Stable, machine-readable error identifier. Safe to branch on. See the table above.

string
Allowed values: invalid_cursor invalid_site_id invalid_order_number invalid_product_id invalid_quantity invalid_service_type updated_since_not_supported invalid_request invalid_submission_id invalid_token insufficient_scope order_not_found product_not_found quote_product_not_found submission_not_found ordering_not_provisioned partner_order_id_reused quote_invalid rate_limit_exceeded internal_error service_unavailable
detail
required

Human-readable explanation. Do not parse this – it may be reworded without notice.

string
requestId
required

Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.

string
Example
{
"type": "about:blank",
"title": "Service Unavailable",
"status": 503,
"code": "service_unavailable",
"detail": "The service is temporarily unavailable. Please retry.",
"requestId": "8f3c1e2a-..."
}