Skip to content

Get shipping prices for a destination and weight

POST
/shipping/quote
curl --request POST \
--url https://api.partners.collaterate.com/v1/shipping/quote \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "siteId": 42, "shipTo": { "country": "US", "state": "MN", "postalCode": "55416" }, "weightOunces": 37.5 }'

The delivery half of a quote. POST /v1/products/{productId}/quote prices the goods; this prices getting them to an address, and returns one price per shipping service the site offers for that destination.

weightOunces is yours to supply, and this API deliberately will not compute it. The catalog’s own weight field – published by GET /v1/products and on each variant of GET /v1/products/{productId} – is recorded in the catalog’s own unit, and the platform stores no unit anywhere: no unit column, no per-site setting, no documented convention. Converting it to ounces would mean inventing a conversion factor, and a wrong factor does not produce an error, it produces a plausible shipping price that is wrong by a constant multiple on every quote. So this endpoint asks, because we genuinely do not know. If you know what unit your own products are kept in, weight x quantity, converted once by you, is exactly the number to send.

siteId is required. Unlike the product quote – where the SLO_/SLS_ id names exactly one site – shipping is priced from SITE configuration (which carriers are enabled, negotiated rates, the site’s own custom shipping options), and productId here is optional, so there is nothing else to resolve it from.

productId is optional and, when sent, selects the fulfilment centre the shipment would originate from, making the quote origin-correct. It must belong to the siteId you sent.

Requires partner-api/products:read – the same scope as the product quote, because a quote is a read that happens to be a POST.

Media typeapplication/json

POST /v1/shipping/quote. Unknown properties are refused by name rather than ignored, both here and inside shipTo.

object
siteId
required

Which of your granted sites to price against. Required: shipping is priced from site configuration – enabled carriers, negotiated rates, the site’s own custom shipping options – and productId is optional, so there is nothing else to resolve it from. A site outside your grant answers 400 invalid_site_id.

integer
>= 1
shipTo
required

Where the shipment is going. The three fields of ShipTo that a carrier actually rates on – there is no name, address1 or city here, because a quote does not need a recipient and this endpoint has no reason to collect end-customer details.

country and state are the same CODES the rest of this API uses: the two-letter code you send on POST /v1/orders, and the same values GET /v1/orders/{orderNumber}/shipments answers with. They are resolved to Collaterate’s own identifiers on our side; state codes are resolved WITHIN the country, since they are not unique across countries.

object
country
required

Two-letter country code, e.g. US, CA, MX.

string
>= 2 characters <= 2 characters
state
required

State or province abbreviation, as spelled within that country, e.g. MN, ON. Required: Collaterate rates on a state, and the code you send is looked up inside the country you sent, so BC means British Columbia under CA and Baja California under MX.

string
>= 1 characters <= 100 characters
postalCode
required

Destination postal code, sent to the carrier as given. A code that does not exist in that country comes back as 422 shipping_quote_unavailable with the carrier’s own message, not as a validation error here.

string
>= 1 characters <= 20 characters
weightOunces
required

Total shipment weight in OUNCES. Required, and deliberately not derived from the catalog: Product.weight is recorded in the catalog’s own unit and the platform stores no unit anywhere, so converting it here would mean inventing a factor and producing a plausible, silently wrong price. Multiply the per-unit weight by your quantity and convert it yourself, in the unit you know your products are kept in.

Decimal, not an integer – half an ounce is a real parcel weight. The upper bound is a sanity guard against a transposed decimal point, not a carrier limit; carriers refuse what they refuse and say so.

number
> 0 <= 5000000
productId

Optional in the schema, but IN PRACTICE YOU ALMOST CERTAINLY NEED TO SEND IT. It selects the fulfilment centre the shipment would originate from, and Collaterate prices from that origin.

Omitting it returns no priced service at all422 shipping_quote_unavailable, with no reason given. Measured 2026-08-18 against five different sites and several weights and destinations: every request without productId came back with an empty service list. There is no “site default origin” for it to fall back to. Send the product you intend to ship.

Sending it is still not a guarantee: the quote also depends on the fulfilment centre that product resolves to being configured for shipping, and some are not. A product whose fulfilment centre cannot be priced returns the same empty 422.

Must be a product on the siteId you sent – a product from another of your sites answers 404 product_not_found, the same as one you were never granted.

string
/^(SLO|SLS)_\d{1,18}$/
Examples
{
"siteId": 42,
"shipTo": {
"country": "US",
"state": "MN",
"postalCode": "55416"
},
"weightOunces": 37.5
}

At least one shipping service was priced. quotes is never empty on a 200 – a destination nothing could be shipped to answers 422 shipping_quote_unavailable instead, so an empty array never has to be told apart from a real answer.

Media typeapplication/json

POST /v1/shipping/quote – one price per shipping service the site offers.

object
quotes
required

Never empty on a 200. When nothing could be priced the response is 422 shipping_quote_unavailable instead, so you never have to tell an empty array apart from a real one.

Array<object>

One priced shipping service.

Collaterate’s own service id and its “is this a carrier service or one of the site’s custom options” flag are deliberately NOT published: the id is a monolith surrogate key that nothing in this API accepts back (POST /v1/orders carries no service selection), and the flag describes how Collaterate is configured rather than what you are buying.

object
serviceName
required

The carrier service as Collaterate names it – what to show a customer.

string
price
required

The price for this service, as a decimal string – the same rule every money field in this API follows. Never parse it into a float.

string
warnings
required

Collaterate’s own reasons that some option could not be priced. Non-empty here means a PARTIAL result – at least one service priced and at least one did not, typically one carrier failing while others answered. Display text, not codes: do not branch on it.

Array<string>
messages
required

Informational notes Collaterate attached to the quote, intended for display. Never errors. Display text, not codes.

Array<string>
Examples
Exampledefault
{
"quotes": [
{
"serviceName": "UPS Ground",
"price": "12.45"
},
{
"serviceName": "UPS 2nd Day Air",
"price": "31.80"
}
],
"warnings": [],
"messages": []
}

The body is malformed, a field is missing or the wrong type (invalid_shipping_quote_request), or siteId is not one of your granted sites (invalid_site_id).

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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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
Examples
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "invalid_shipping_quote_request",
"detail": "\"weightOunces\" is required and must be a number.",
"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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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-..."
}

You sent a productId that does not exist, sits outside your granted sites, or belongs to a different site than the siteId you sent. All three answer identically (product_not_found); see the tenancy guide.

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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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",
"code": "all_segments_visible_reset_not_acknowledged"
}

The request was well-formed but could not be answered: a country or state code Collaterate does not recognise (unknown_country_code, unknown_state_code), or a destination and weight for which no shipping service could be priced at all (shipping_quote_unavailable, with Collaterate’s own reasons in detail).

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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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
Examples
{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"code": "unknown_state_code",
"detail": "\"shipTo.state\" is not a state or province of \"US\": \"ZZ\".",
"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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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_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
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: all_segments_visible_reset_not_acknowledged artwork_reservation_not_usable audit_unavailable auto_assigned_segments_present credit_account_not_found cursor_not_supported_with_search denial_reason_not_available division_code_conflict division_create_requires_address division_not_found division_not_on_site division_parent_invalid image_not_found insufficient_scope internal_error invalid_active invalid_adjustment_type invalid_cursor invalid_credit_account_id invalid_division_id invalid_image_id invalid_image_patch invalid_item_number invalid_order_number invalid_product_id invalid_product_patch invalid_quantity invalid_query invalid_request invalid_segment_ids invalid_segments_body invalid_service_type invalid_shipping_quote_request invalid_site_id invalid_submission_id invalid_token invalid_user_id invalid_variant_id invalid_variant_patch monolith_rejected monolith_target_missing null_not_supported order_item_cancelled order_item_not_found order_not_found ordering_not_provisioned parent_is_self parent_not_on_site partner_order_id_reused product_not_found product_write_partially_applied proof_not_pending_review quote_invalid quote_product_not_found rate_limit_exceeded search_not_available segment_not_on_site service_unavailable shipping_quote_unavailable site_id_required_for_search site_not_found sls_null_backfills_master submission_not_found template_not_found unknown_country_code unknown_query_parameter unknown_state_code unsupported_field_for_service_type updated_since_not_supported user_already_exists user_not_found variant_not_site_addressable
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-..."
}