Get a single order by number
const url = 'https://api.partners.collaterate.com/v1/orders/1';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://api.partners.collaterate.com/v1/orders/1 \ --header 'Authorization: Bearer <token>'Returns the order only if it belongs to one of your granted sites. An order
that exists in a site you cannot see returns the identical 404 order_not_found as
an order number that does not exist at all – see “Scoping and the 404-vs-403 rule”
above.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Responses
Section titled “Responses”The order.
object
When the order was placed. Set once, monotonic – this is what pagination orders by.
Present for reference only. Do not use this field to detect changes to an order you have already fetched – it does not reliably move when a shipment, tracking number, or item quantity changes on an existing order. There is currently no supported change-detection mechanism for existing orders.
The identifier the system that created this order set on it. For an order you
submitted through POST /v1/orders, this is the partnerOrderId you sent – so this
is how you look an order up by your own reference rather than ours.
It is deliberately NOT called partnerOrderId, because it is not scoped to you and
is not guaranteed unique: any system that creates an order on this site can set it,
and an order that did not come through this API carries whatever that system chose.
Match on it, but do not assume a match is unique or that a populated value came from
you. null on the majority of orders, which were not created with one.
All seven fields are decimal amounts encoded as strings, not JSON numbers. The
underlying column type is numeric(24,10); a JSON/JavaScript number is a 64-bit
float and cannot represent that precision without silent rounding, which is not an
acceptable outcome for an invoice total. Parse these with a decimal library in your
own language – never with parseFloat/Number() or an equivalent binary-float
conversion.
These components reconcile, and you can rely on it:
item + shipping + tax - discount + handling - creditAccount = order
Note the signs: discount and creditAccount are published as positive amounts and are
SUBTRACTED; handling is added.
The identity is reliable for recent orders and NOT for old ones, and the boundary is worth knowing before you backfill. Measured per year: from 2019 onward it holds on 99.97%-100% of live orders. Before that it fails routinely – roughly 97,000 orders going back to 2007 do not reconcile, because their components predate the current pricing model rather than because a field is missing. If you are paging through history, expect it, and do not treat a pre-2019 mismatch as a bug to report.
Among recent failures there is one recognisable shape: an order whose order was zeroed
after the fact while its components were left in place, so the left-hand side comes out
negative against an order of "0.0000000000". That is a cancelled or written-off
order. It explains only a minority of the historical mismatches, so do not assume it
covers every one you see.
Treat a null in any component as “not computed”, not as zero, before reconciling.
object
null when the total has not been computed for this order. That is NOT the same
as zero and must not be treated as such – an order awaiting pricing has no item
total, whereas an order genuinely costing nothing has "0.0000000000".
null when not computed. See item.
null when not computed. See item.
Every discount applied to the order, as a positive amount that has ALREADY been
subtracted from order. Do not add it to anything – order is final.
This is not the same thing as a promotion code, and the difference matters. Just
under half of all discounted orders carry no promotion code at all: the reduction came
from a user discount, a loyalty coupon, or a product-level pricing discount. Use
discounts on GET /v1/orders/{orderNumber} to see the promotion codes specifically,
and expect their sum to be less than this figure whenever another kind of discount is
also in play.
null when not computed. See item.
Handling charged on the order, as a positive amount already INCLUDED in order.
Nonzero on roughly one order in nine.
null when not computed. See item.
Credit-account balance applied at checkout, as a positive amount already
SUBTRACTED from order.
Published separately from discount because it is a different mechanism: discount
reduces the price of what was bought, whereas this is an account balance being spent
against the bill. A partner reconciling to an invoice needs them apart. Nonzero on
roughly one order in twenty-three.
null when not computed. See item.
The amount payable, after discount and creditAccount have been taken off and
handling added. See the reconciliation identity on this schema. null when not
computed. See item.
The division this order was placed under, or null if it has none. Set at
submission via POST /v1/orders’s divisionId; look up its name/code with
GET /v1/divisions.
The id of the user who placed the order, or null if none is attached. Resolve
their name, email and company with GET /v1/users/{userId}.
Whether the order requires approval, or null if it is outside any approval workflow.
Whether the order has been approved, or null if no approval decision has been recorded.
The ordering customer’s first name, or null when the order has no user. Resolved
from the user on the order’s own site; see also userId.
The ordering customer’s last name, or null when the order has no user.
The ordering customer’s email, or null when the order has no user or none is recorded.
The promotions behind this order’s discount. [] means none were applied.
This does not necessarily account for totals.discount, and often will not.
Just under half of all discounted orders have no promotion at all – their
reduction came from a user discount, a loyalty coupon, or a product-level pricing
discount, none of which appears here. totals.discount is the authoritative
figure; this array itemises only the promotion-shaped part of it. Do not
reconcile one against the other and treat a mismatch as an error.
Almost always one entry; the observed maximum anywhere is three.
One promotion applied to an order, as embedded in OrderDetail.
object
The code that was entered, or null for an automatic promotion that applies
without anyone typing anything. About one row in eleven has no code, so null here is
a normal state rather than a missing value – read name to see what was applied.
The promotion’s name. Always present, including on codeless rows.
A positive decimal string, already subtracted from the order total. Same precision
rule as OrderTotals – parse it with a decimal library, never parseFloat.
Example
{ "externalId": "PO-2026-00417", "totals": { "item": "1024.5000000000", "shipping": "42.0000000000", "tax": "81.9600000000", "discount": "102.4500000000", "handling": "12.5000000000", "creditAccount": "50.0000000000", "order": "1148.4600000000" }, "discounts": [ { "code": "SPRING25", "name": "Spring 25% off", "amount": "102.4500000000" } ]}The order number in the path is not a positive integer. Note that a well-formed
order number you are not entitled to see is a 404, not a 400 – this status
means the path segment itself could not be read as a number. This route accepts no query parameters at all – sending one is unknown_query_parameter, not silently ignored.
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 shipTo – or 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_shipping_quote_request |
400 | POST /v1/shipping/quote’s body failed validation – malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces outside 0 < w <= 5000000. detail names the field. |
No – fix the request body. |
invalid_item_number |
400 | The itemNumber path segment on a proof route is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never a Collaterate internal id. |
No – fix the path. |
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. |
order_item_not_found |
404 | No line matching (orderNumber, itemNumber) resolves for you – absent, outside your granted sites, or hidden from the customer, identically. |
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. |
template_not_found |
404 | GET /v1/products/{productId}/template found no Chili template for this product – not TEMPLATE-classified, TEMPLATE-classified with a legacy EDOC/CANVA vendor, or Chili-templated with no document configured. All three identically. |
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. |
proof_not_pending_review |
409 | A proof approve or decline was sent for a line whose proofStatus is not PENDING_REVIEW – no proof yet, already decided, or a state this API does not recognise. detail says which. On a RETRY of a decision that already succeeded, this is the expected answer and means success. |
No – read the line’s current proofStatus from GET /v1/orders/{orderNumber}/items. |
order_item_cancelled |
409 | A proof approve or decline was sent for a cancelled line. It will never be produced, so its proof cannot be decided. | No. |
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). |
unknown_country_code |
422 | POST /v1/shipping/quote’s shipTo.country is not a country code Collaterate recognises. |
No – send a code Collaterate carries (US, CA, MX, …). |
unknown_state_code |
422 | POST /v1/shipping/quote’s shipTo.state is not a state or province of the country you sent. State codes are resolved WITHIN the country, since they are not unique across countries. |
No – fix the state, or the country it belongs to. |
shipping_quote_unavailable |
422 | POST /v1/shipping/quote produced no priced service at all for that destination and weight. detail carries Collaterate’s own reasons when it gave any – an invalid postal code for the country is the common one – and says so plainly when it did not. |
No – fix the destination. An empty result is never returned as a 200. |
denial_reason_not_available |
422 | POST .../proof/decline’s reason is well-formed but is not a denial reason this line’s proof offers – including a code that is valid on a different line, and UNKNOWN, which never is. detail lists what is valid here. |
No – pick a code from GET .../proof/denial-reasons for this line. |
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
Always the literal string about:blank today; reserved by RFC 9457 for future use.
Short, human-readable summary of the HTTP status (e.g. “Not Found”).
The HTTP status code, repeated in the body for convenience.
Stable, machine-readable error identifier. Safe to branch on. See the table above.
Human-readable explanation. Do not parse this – it may be reworded without notice.
Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.
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.
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 shipTo – or 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_shipping_quote_request |
400 | POST /v1/shipping/quote’s body failed validation – malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces outside 0 < w <= 5000000. detail names the field. |
No – fix the request body. |
invalid_item_number |
400 | The itemNumber path segment on a proof route is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never a Collaterate internal id. |
No – fix the path. |
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. |
order_item_not_found |
404 | No line matching (orderNumber, itemNumber) resolves for you – absent, outside your granted sites, or hidden from the customer, identically. |
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. |
template_not_found |
404 | GET /v1/products/{productId}/template found no Chili template for this product – not TEMPLATE-classified, TEMPLATE-classified with a legacy EDOC/CANVA vendor, or Chili-templated with no document configured. All three identically. |
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. |
proof_not_pending_review |
409 | A proof approve or decline was sent for a line whose proofStatus is not PENDING_REVIEW – no proof yet, already decided, or a state this API does not recognise. detail says which. On a RETRY of a decision that already succeeded, this is the expected answer and means success. |
No – read the line’s current proofStatus from GET /v1/orders/{orderNumber}/items. |
order_item_cancelled |
409 | A proof approve or decline was sent for a cancelled line. It will never be produced, so its proof cannot be decided. | No. |
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). |
unknown_country_code |
422 | POST /v1/shipping/quote’s shipTo.country is not a country code Collaterate recognises. |
No – send a code Collaterate carries (US, CA, MX, …). |
unknown_state_code |
422 | POST /v1/shipping/quote’s shipTo.state is not a state or province of the country you sent. State codes are resolved WITHIN the country, since they are not unique across countries. |
No – fix the state, or the country it belongs to. |
shipping_quote_unavailable |
422 | POST /v1/shipping/quote produced no priced service at all for that destination and weight. detail carries Collaterate’s own reasons when it gave any – an invalid postal code for the country is the common one – and says so plainly when it did not. |
No – fix the destination. An empty result is never returned as a 200. |
denial_reason_not_available |
422 | POST .../proof/decline’s reason is well-formed but is not a denial reason this line’s proof offers – including a code that is valid on a different line, and UNKNOWN, which never is. detail lists what is valid here. |
No – pick a code from GET .../proof/denial-reasons for this line. |
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
Always the literal string about:blank today; reserved by RFC 9457 for future use.
Short, human-readable summary of the HTTP status (e.g. “Not Found”).
The HTTP status code, repeated in the body for convenience.
Stable, machine-readable error identifier. Safe to branch on. See the table above.
Human-readable explanation. Do not parse this – it may be reworded without notice.
Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.
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.
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 shipTo – or 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_shipping_quote_request |
400 | POST /v1/shipping/quote’s body failed validation – malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces outside 0 < w <= 5000000. detail names the field. |
No – fix the request body. |
invalid_item_number |
400 | The itemNumber path segment on a proof route is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never a Collaterate internal id. |
No – fix the path. |
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. |
order_item_not_found |
404 | No line matching (orderNumber, itemNumber) resolves for you – absent, outside your granted sites, or hidden from the customer, identically. |
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. |
template_not_found |
404 | GET /v1/products/{productId}/template found no Chili template for this product – not TEMPLATE-classified, TEMPLATE-classified with a legacy EDOC/CANVA vendor, or Chili-templated with no document configured. All three identically. |
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. |
proof_not_pending_review |
409 | A proof approve or decline was sent for a line whose proofStatus is not PENDING_REVIEW – no proof yet, already decided, or a state this API does not recognise. detail says which. On a RETRY of a decision that already succeeded, this is the expected answer and means success. |
No – read the line’s current proofStatus from GET /v1/orders/{orderNumber}/items. |
order_item_cancelled |
409 | A proof approve or decline was sent for a cancelled line. It will never be produced, so its proof cannot be decided. | No. |
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). |
unknown_country_code |
422 | POST /v1/shipping/quote’s shipTo.country is not a country code Collaterate recognises. |
No – send a code Collaterate carries (US, CA, MX, …). |
unknown_state_code |
422 | POST /v1/shipping/quote’s shipTo.state is not a state or province of the country you sent. State codes are resolved WITHIN the country, since they are not unique across countries. |
No – fix the state, or the country it belongs to. |
shipping_quote_unavailable |
422 | POST /v1/shipping/quote produced no priced service at all for that destination and weight. detail carries Collaterate’s own reasons when it gave any – an invalid postal code for the country is the common one – and says so plainly when it did not. |
No – fix the destination. An empty result is never returned as a 200. |
denial_reason_not_available |
422 | POST .../proof/decline’s reason is well-formed but is not a denial reason this line’s proof offers – including a code that is valid on a different line, and UNKNOWN, which never is. detail lists what is valid here. |
No – pick a code from GET .../proof/denial-reasons for this line. |
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
Always the literal string about:blank today; reserved by RFC 9457 for future use.
Short, human-readable summary of the HTTP status (e.g. “Not Found”).
The HTTP status code, repeated in the body for convenience.
Stable, machine-readable error identifier. Safe to branch on. See the table above.
Human-readable explanation. Do not parse this – it may be reworded without notice.
Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.
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.
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 shipTo – or 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_shipping_quote_request |
400 | POST /v1/shipping/quote’s body failed validation – malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces outside 0 < w <= 5000000. detail names the field. |
No – fix the request body. |
invalid_item_number |
400 | The itemNumber path segment on a proof route is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never a Collaterate internal id. |
No – fix the path. |
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. |
order_item_not_found |
404 | No line matching (orderNumber, itemNumber) resolves for you – absent, outside your granted sites, or hidden from the customer, identically. |
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. |
template_not_found |
404 | GET /v1/products/{productId}/template found no Chili template for this product – not TEMPLATE-classified, TEMPLATE-classified with a legacy EDOC/CANVA vendor, or Chili-templated with no document configured. All three identically. |
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. |
proof_not_pending_review |
409 | A proof approve or decline was sent for a line whose proofStatus is not PENDING_REVIEW – no proof yet, already decided, or a state this API does not recognise. detail says which. On a RETRY of a decision that already succeeded, this is the expected answer and means success. |
No – read the line’s current proofStatus from GET /v1/orders/{orderNumber}/items. |
order_item_cancelled |
409 | A proof approve or decline was sent for a cancelled line. It will never be produced, so its proof cannot be decided. | No. |
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). |
unknown_country_code |
422 | POST /v1/shipping/quote’s shipTo.country is not a country code Collaterate recognises. |
No – send a code Collaterate carries (US, CA, MX, …). |
unknown_state_code |
422 | POST /v1/shipping/quote’s shipTo.state is not a state or province of the country you sent. State codes are resolved WITHIN the country, since they are not unique across countries. |
No – fix the state, or the country it belongs to. |
shipping_quote_unavailable |
422 | POST /v1/shipping/quote produced no priced service at all for that destination and weight. detail carries Collaterate’s own reasons when it gave any – an invalid postal code for the country is the common one – and says so plainly when it did not. |
No – fix the destination. An empty result is never returned as a 200. |
denial_reason_not_available |
422 | POST .../proof/decline’s reason is well-formed but is not a denial reason this line’s proof offers – including a code that is valid on a different line, and UNKNOWN, which never is. detail lists what is valid here. |
No – pick a code from GET .../proof/denial-reasons for this line. |
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
Always the literal string about:blank today; reserved by RFC 9457 for future use.
Short, human-readable summary of the HTTP status (e.g. “Not Found”).
The HTTP status code, repeated in the body for convenience.
Stable, machine-readable error identifier. Safe to branch on. See the table above.
Human-readable explanation. Do not parse this – it may be reworded without notice.
Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.
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.
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 shipTo – or 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_shipping_quote_request |
400 | POST /v1/shipping/quote’s body failed validation – malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces outside 0 < w <= 5000000. detail names the field. |
No – fix the request body. |
invalid_item_number |
400 | The itemNumber path segment on a proof route is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never a Collaterate internal id. |
No – fix the path. |
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. |
order_item_not_found |
404 | No line matching (orderNumber, itemNumber) resolves for you – absent, outside your granted sites, or hidden from the customer, identically. |
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. |
template_not_found |
404 | GET /v1/products/{productId}/template found no Chili template for this product – not TEMPLATE-classified, TEMPLATE-classified with a legacy EDOC/CANVA vendor, or Chili-templated with no document configured. All three identically. |
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. |
proof_not_pending_review |
409 | A proof approve or decline was sent for a line whose proofStatus is not PENDING_REVIEW – no proof yet, already decided, or a state this API does not recognise. detail says which. On a RETRY of a decision that already succeeded, this is the expected answer and means success. |
No – read the line’s current proofStatus from GET /v1/orders/{orderNumber}/items. |
order_item_cancelled |
409 | A proof approve or decline was sent for a cancelled line. It will never be produced, so its proof cannot be decided. | No. |
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). |
unknown_country_code |
422 | POST /v1/shipping/quote’s shipTo.country is not a country code Collaterate recognises. |
No – send a code Collaterate carries (US, CA, MX, …). |
unknown_state_code |
422 | POST /v1/shipping/quote’s shipTo.state is not a state or province of the country you sent. State codes are resolved WITHIN the country, since they are not unique across countries. |
No – fix the state, or the country it belongs to. |
shipping_quote_unavailable |
422 | POST /v1/shipping/quote produced no priced service at all for that destination and weight. detail carries Collaterate’s own reasons when it gave any – an invalid postal code for the country is the common one – and says so plainly when it did not. |
No – fix the destination. An empty result is never returned as a 200. |
denial_reason_not_available |
422 | POST .../proof/decline’s reason is well-formed but is not a denial reason this line’s proof offers – including a code that is valid on a different line, and UNKNOWN, which never is. detail lists what is valid here. |
No – pick a code from GET .../proof/denial-reasons for this line. |
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
Always the literal string about:blank today; reserved by RFC 9457 for future use.
Short, human-readable summary of the HTTP status (e.g. “Not Found”).
The HTTP status code, repeated in the body for convenience.
Stable, machine-readable error identifier. Safe to branch on. See the table above.
Human-readable explanation. Do not parse this – it may be reworded without notice.
Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.
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-..."}Headers
Section titled “Headers”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.
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 shipTo – or 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_shipping_quote_request |
400 | POST /v1/shipping/quote’s body failed validation – malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces outside 0 < w <= 5000000. detail names the field. |
No – fix the request body. |
invalid_item_number |
400 | The itemNumber path segment on a proof route is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never a Collaterate internal id. |
No – fix the path. |
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. |
order_item_not_found |
404 | No line matching (orderNumber, itemNumber) resolves for you – absent, outside your granted sites, or hidden from the customer, identically. |
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. |
template_not_found |
404 | GET /v1/products/{productId}/template found no Chili template for this product – not TEMPLATE-classified, TEMPLATE-classified with a legacy EDOC/CANVA vendor, or Chili-templated with no document configured. All three identically. |
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. |
proof_not_pending_review |
409 | A proof approve or decline was sent for a line whose proofStatus is not PENDING_REVIEW – no proof yet, already decided, or a state this API does not recognise. detail says which. On a RETRY of a decision that already succeeded, this is the expected answer and means success. |
No – read the line’s current proofStatus from GET /v1/orders/{orderNumber}/items. |
order_item_cancelled |
409 | A proof approve or decline was sent for a cancelled line. It will never be produced, so its proof cannot be decided. | No. |
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). |
unknown_country_code |
422 | POST /v1/shipping/quote’s shipTo.country is not a country code Collaterate recognises. |
No – send a code Collaterate carries (US, CA, MX, …). |
unknown_state_code |
422 | POST /v1/shipping/quote’s shipTo.state is not a state or province of the country you sent. State codes are resolved WITHIN the country, since they are not unique across countries. |
No – fix the state, or the country it belongs to. |
shipping_quote_unavailable |
422 | POST /v1/shipping/quote produced no priced service at all for that destination and weight. detail carries Collaterate’s own reasons when it gave any – an invalid postal code for the country is the common one – and says so plainly when it did not. |
No – fix the destination. An empty result is never returned as a 200. |
denial_reason_not_available |
422 | POST .../proof/decline’s reason is well-formed but is not a denial reason this line’s proof offers – including a code that is valid on a different line, and UNKNOWN, which never is. detail lists what is valid here. |
No – pick a code from GET .../proof/denial-reasons for this line. |
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
Always the literal string about:blank today; reserved by RFC 9457 for future use.
Short, human-readable summary of the HTTP status (e.g. “Not Found”).
The HTTP status code, repeated in the body for convenience.
Stable, machine-readable error identifier. Safe to branch on. See the table above.
Human-readable explanation. Do not parse this – it may be reworded without notice.
Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.
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.
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 shipTo – or 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_shipping_quote_request |
400 | POST /v1/shipping/quote’s body failed validation – malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces outside 0 < w <= 5000000. detail names the field. |
No – fix the request body. |
invalid_item_number |
400 | The itemNumber path segment on a proof route is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never a Collaterate internal id. |
No – fix the path. |
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. |
order_item_not_found |
404 | No line matching (orderNumber, itemNumber) resolves for you – absent, outside your granted sites, or hidden from the customer, identically. |
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. |
template_not_found |
404 | GET /v1/products/{productId}/template found no Chili template for this product – not TEMPLATE-classified, TEMPLATE-classified with a legacy EDOC/CANVA vendor, or Chili-templated with no document configured. All three identically. |
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. |
proof_not_pending_review |
409 | A proof approve or decline was sent for a line whose proofStatus is not PENDING_REVIEW – no proof yet, already decided, or a state this API does not recognise. detail says which. On a RETRY of a decision that already succeeded, this is the expected answer and means success. |
No – read the line’s current proofStatus from GET /v1/orders/{orderNumber}/items. |
order_item_cancelled |
409 | A proof approve or decline was sent for a cancelled line. It will never be produced, so its proof cannot be decided. | No. |
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). |
unknown_country_code |
422 | POST /v1/shipping/quote’s shipTo.country is not a country code Collaterate recognises. |
No – send a code Collaterate carries (US, CA, MX, …). |
unknown_state_code |
422 | POST /v1/shipping/quote’s shipTo.state is not a state or province of the country you sent. State codes are resolved WITHIN the country, since they are not unique across countries. |
No – fix the state, or the country it belongs to. |
shipping_quote_unavailable |
422 | POST /v1/shipping/quote produced no priced service at all for that destination and weight. detail carries Collaterate’s own reasons when it gave any – an invalid postal code for the country is the common one – and says so plainly when it did not. |
No – fix the destination. An empty result is never returned as a 200. |
denial_reason_not_available |
422 | POST .../proof/decline’s reason is well-formed but is not a denial reason this line’s proof offers – including a code that is valid on a different line, and UNKNOWN, which never is. detail lists what is valid here. |
No – pick a code from GET .../proof/denial-reasons for this line. |
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
Always the literal string about:blank today; reserved by RFC 9457 for future use.
Short, human-readable summary of the HTTP status (e.g. “Not Found”).
The HTTP status code, repeated in the body for convenience.
Stable, machine-readable error identifier. Safe to branch on. See the table above.
Human-readable explanation. Do not parse this – it may be reworded without notice.
Echoes the request’s id. Include this when contacting Partner Integrations about a specific failed call – it resolves directly to one log entry.
Example
{ "type": "about:blank", "title": "Service Unavailable", "status": 503, "code": "service_unavailable", "detail": "The service is temporarily unavailable. Please retry.", "requestId": "8f3c1e2a-..."}