Skip to content

Errors

Every error this API returns is an RFC 9457 problem document, served as Content-Type: application/problem+json. That includes the 401 from the authorizer and the 429 from the throttle, which are produced by the gateway rather than by application code but are shaped identically - so one parser and one branch on code handles the whole API. The sole exception is the edge firewall’s 403, described at the end.

{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "invalid_cursor",
"detail": "The \"cursor\" query parameter is malformed.",
"requestId": "8f3c1e2a-3d4b-4a1e-9f77-2b6c5e0a1d33"
}
Field Notes
type Always the literal string about:blank today. Reserved by RFC 9457 for a future problem-type URI.
title Short human summary of the status, for example Not Found. Derived from status; carries no information status does not.
status The HTTP status code, repeated in the body.
code The stable, machine-readable identifier. This is the field to branch on.
detail Human-readable prose. May be reworded at any time. Do not parse it.
requestId Echoes the request’s id. Include it when you escalate a specific failure.

Fix and resend means retrying the identical request will produce the identical response. Retry means the request was fine and the condition is transient. Back off means retry, but not immediately.

code Status What happened Action
invalid_cursor 400 The cursor you sent could not be decoded. Usually truncated, edited, or carried over from a different query shape. Fix and resend 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. Fix and resend Check GET /v1/me’s grantedSiteIds.
invalid_order_number 400 The order number in the path is not a positive integer. Fix and resend
invalid_product_id 400 The productId path segment does not match the required SLO_/SLS_-prefixed shape. Fix and resend
invalid_service_type 400 The serviceType filter on GET /v1/products is not one of the values this API can order (STOCK or POD). Fix and resend
invalid_query 400 The q search parameter is empty or contains only whitespace. Fix and resend Send a search term, or omit q to list the catalog.
site_id_required_for_search 400 q was sent without siteId. Listing spans your whole grant; searching is per site, because relevance scores from different sites are not comparable. Fix and resend Add siteId, or call GET /v1/sites/{siteId}/products?q=.
cursor_not_supported_with_search 400 q was sent together with cursor. A search returns a ranked result set rather than a page, so nextCursor is always null and there is nothing to resume. Fix and resend Drop the cursor and raise limit instead.
search_not_available 400 q was sent in an environment where the search service is not available. Not a fault in your request, and not a transient failure either – retrying will not change it. Fix and resend Omit q and page the catalog; ask Partner Integrations when search reaches this environment.
updated_since_not_supported 400 You sent an updatedSince parameter. It is rejected rather than ignored, on purpose. Fix and resend See why below.
invalid_request 400 A request body failed validation - malformed JSON, a missing or malformed field, an unknown key, or a body-level rule such as partnerLineId uniqueness. Also a malformed query or path parameter on routes that have not been given their own code yet - the submissions list’s status/limit filter, GET /v1/orders’s placedFrom/placedTo, and the proof and artwork bodies. Fix and resend detail names the exact violation.
unknown_query_parameter 400 A query parameter this endpoint does not accept. Rejected rather than ignored: a filter that is silently dropped returns a full result set that looks filtered. detail names the parameter and lists the accepted ones. Fix and resend Check the spelling and the case - siteId, not siteid.
invalid_submission_id 400 The submissionId path segment is not a well-formed UUID. Fix and resend
invalid_product_patch 400 The PATCH /v1/products/{productId} body was empty, named a field that is not writable, or gave one the wrong type. detail names the field. Fix and resend The writable set is a strict allow-list - see Catalog and products.
invalid_division_id 400 The divisionId path segment or query parameter - or GET /v1/divisions’s parentId, which names a division too - is not a positive whole number of at most nine digits. Fix and resend Use a divisionId from GET /v1/divisions.
invalid_credit_account_id 400 The creditAccountId path segment is not a positive whole number of at most nine digits. Fix and resend Use a creditAccountId from GET /v1/credit-accounts.
invalid_active 400 The active filter on GET /v1/credit-accounts is something other than exactly true or false. Fix and resend Rejected rather than coerced - 1, yes and on all disagree with each other about what 0 means.
invalid_adjustment_type 400 The type filter on the adjustments list is not an upper-case movement code. Fix and resend Codes look like PRODUCT_DEBIT. An unknown but well-formed code is accepted and returns an empty page.
invalid_user_id 400 The userId path segment or query parameter is not a positive whole number of at most nine digits. Nine is the cap because the column is a 32-bit integer - a longer value would be a database error rather than a clean “not found”. Fix and resend Use a userId from GET /v1/users.
invalid_variant_patch 400 The PATCH /v1/products/{productId}/variants/{variantId} body was empty, named a field that is not writable on a variant, or gave one the wrong type. detail names the field. Fix and resend Five fields are writable: attribute1Choice, attribute2Choice, attribute3Choice, location, weight. See Catalog and products.
invalid_variant_id 400 The variantId path segment is not a positive whole number. Print-on-demand products publish variantId: null and have no per-site addressable variant. Fix and resend Use a variantId from the product’s variants array.
invalid_image_patch 400 The PATCH /v1/products/{productId}/images/{imageId} body was empty, named a field that is not writable on an image, or gave one the wrong type. detail names the field. Fix and resend Three fields are writable: imageAlt, sequence, primary. imagePath, attribution and the dimensions are read-only.
invalid_image_id 400 The imageId path segment is not a positive whole number. Fix and resend Use an imageId from GET /v1/products/{productId}/images.
division_create_requires_address 400 That site stores a postal address on every division, and Collaterate requires it at creation. This API does not manage division addresses, so it cannot create a division there. Not retryable Ask Partner Integrations to create the division.
invalid_segment_ids 400 One or more ids in PUT /v1/products/{productId}/segments is not a segment on that product’s site. detail names them. Nothing was changed - the whole request is refused rather than applying the ids that did resolve. Fix and resend Use GET /v1/segments for the ids you can assign. An id belonging to another site and an id that does not exist answer identically, on purpose.
invalid_segments_body 400 The PUT /v1/products/{productId}/segments body is malformed - segmentIds missing or not an array, a member that is not a positive whole number, or an unrecognised field. Fix and resend The only accepted field is segmentIds. An unknown key is refused rather than ignored because this endpoint replaces the assignment.
monolith_rejected 400 Collaterate refused the change on its own validation grounds. detail carries Collaterate’s message and, where it named one, the field. Fix and resend
null_not_supported 400 You sent null for a field that can be set but not cleared - visibility, requiresApproval, overrideDisplay, minQuantity, maxQuantity - or for any field of a proof or artwork request body. Fix and resend Send a value, or omit the field. Generated clients that serialise an absent optional as null are the usual cause.
sls_null_backfills_master 400 On a share-backed (SLS_) product you sent null for description, metaDescription or metaKeywords. That does not restore inheritance from the master product - it copies the master’s current value into your share, which then stops tracking it. Fix and resend Send "overrideDisplay": false to restore inheritance for those fields together. See Catalog and products.
invalid_quantity 400 The quantity on a product quote request is missing or not a positive integer. Fix and resend
invalid_shipping_quote_request 400 The POST /v1/shipping/quote body failed validation - malformed JSON, a missing or mistyped field, an unknown key at the top level or inside shipTo, or a weightOunces that is not greater than 0 and at most 5,000,000. Fix and resend detail names the field.
invalid_item_number 400 The itemNumber in a proof or artwork route’s path is not a positive integer. It is the line’s itemNumber from GET /v1/orders/{orderNumber}/items, never an internal id. Fix and resend
invalid_token 401 Your credential is missing, expired, or otherwise not accepted. Produced by the authorizer, so detail is generic. Mint a fresh token and retry once. If that fails too, the credential needs attention.
insufficient_scope 403 Your credential is valid but its effective scopes do not include the one this operation requires. Fix and resend Mint a token carrying the scope, or ask Partner Integrations to add the capability.
order_not_found 404 No such order, or an order that exists outside your granted sites. These are indistinguishable by design. Fix and resend Do not treat as proof of non-existence.
order_item_not_found 404 No line matching (orderNumber, itemNumber) is visible to you - absent, outside your granted sites, or hidden from the customer, indistinguishably. Fix and resend Do not treat as proof of non-existence.
product_not_found 404 No such product, or one that exists outside your granted sites - the same rule as order_not_found. Fix and resend Do not treat as proof of non-existence. See Tenancy and access.
quote_product_not_found 404 The product resolved in our catalog, but Collaterate could not price it. Rare. Escalate with the requestId if it persists. See Pricing.
image_not_found 404 The imageId is not one of that product’s images. Returned identically whether the image belongs to a different product or does not exist. Not retryable List the product’s images with GET /v1/products/{productId}/images. Images are added and removed in the Collaterate admin UI, not through this API.
division_not_found 404 No such division on your granted sites. Returned identically whether it does not exist or sits on a site you were not granted. Not retryable List with GET /v1/divisions.
division_code_conflict 409 A division with that code already exists on the site. Codes match users at sign-in, so a duplicate makes that match ambiguous. Fix and resend Choose another code. The check is best-effort - the platform enforces no constraint itself.
credit_account_not_found 404 No such credit account on your granted sites. Returned identically whether it does not exist or sits on a site you were not granted. Not retryable List with GET /v1/credit-accounts.
all_segments_visible_reset_not_acknowledged 409 The user currently has allSegmentsVisible: true, and assigning specific segments would set it to false - which Collaterate does even when the list is unchanged. Fix and resend Resend with "acknowledgeAllSegmentsVisibleReset": true if you intend the change.
auto_assigned_segments_present 409 Your segmentIds would remove a segment that a site rule assigned automatically. detail names the ids. Fix and resend Include those ids to keep them.
user_already_exists 409 A user with that username or email already exists on that site. Checked before anything is created. Not retryable The check is best-effort: it can occasionally miss a duplicate the platform then rejects as monolith_rejected.
user_not_found 404 No such user on your granted sites. Returned identically whether the user does not exist or exists on a site you were not granted. Not retryable List with GET /v1/users.
site_not_found 404 No such site among your granted sites. Returned identically whether it does not exist or is simply not one of your granted sites. Not retryable List with GET /v1/sites.
template_not_found 404 No Chili template is configured for this product - whether it is not template-classified, uses a legacy vendor, or is template-classified with no document set up. Not fixable by retrying. See Catalog and products.
submission_not_found 404 No submission exists with this id for your credential, whether it never existed or belongs to another partner - indistinguishable by design. Fix and resend Do not treat as proof of non-existence.
ordering_not_provisioned 409 The order’s siteId has no ordering user configured. Set up per (partner, site) by staff at onboarding; a partner cannot provision it themselves. Ask Partner Integrations to provision an ordering user for the site, then retry. See Submitting orders.
partner_order_id_reused 409 The partnerOrderId was already used, with a request body that does not match this one. Fix and resend Use a new partnerOrderId, or resend the exact original body to poll the existing submission. See Submitting orders.
proof_not_pending_review 409 You approved or declined a proof on a line whose proofStatus is not PENDING_REVIEW - no proof yet, already decided, or a state we do not recognise. detail says which. Fix and resend On a retry of a decision that already succeeded, this is the expected answer: treat it as success. Otherwise read the line’s current proofStatus. See Approving a proof.
order_item_cancelled 409 You acted on a cancelled line - approving or declining its proof, or attaching artwork to it. It will never be produced, so neither is meaningful. Fix and resend Check the cancelled flag on the line in GET /v1/orders/{orderNumber}/items.
artwork_reservation_not_usable 409 The reservationName on POST .../artwork cannot be attached: the upload has not finished, the name is unknown, or it has already been attached - a reservation is single-use. detail carries Collaterate’s own wording. Fix and resend Confirm the PUT to uploadUrl returned 200, then attach. If the URL had expired, request a new one and upload again. See Artwork and print files.
monolith_target_missing 409 The product resolved in our catalog but Collaterate no longer has it, so the two stores disagree. Deliberately not a 404: the catalog still lists it. Escalate with the requestId.
product_write_partially_applied 409 Your change spanned several upstream sections and a later one was refused after an earlier one had already been saved. detail names the sections that applied. Fix and resend These writes are not atomic. Re-send the fields that did not apply rather than assuming nothing happened.
quote_invalid 422 Collaterate rejected the quote on business-validation grounds, for example a quantity over the product’s configured maximum. Fix and resend detail carries Collaterate’s own message. See Pricing.
unknown_country_code 422 shipTo.country on a shipping quote is not a country code Collaterate recognises. Fix and resend Use a two-letter code Collaterate carries (US, CA, MX, …).
unknown_state_code 422 shipTo.state on a shipping quote is not a state or province of the country you sent. Codes are resolved within the country, because they are not unique across them - BC is British Columbia under CA and Baja California under MX. Fix and resend Check the state against the country.
shipping_quote_unavailable 422 A 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. Fix and resend An empty result is never returned as a 200, so you never have to tell it apart from a real one. See Pricing.
denial_reason_not_available 422 The reason on a proof decline is well-formed but is not one that line’s proof offers - including a code valid on a different line, and UNKNOWN, which never is. Fix and resend detail lists the codes valid for that line. Read them per line, not once per account. See Approving a proof.
division_parent_invalid 422 The parentId is not a division on that site, or is already nested at the 10-level depth limit. Absent and foreign parents answer identically. Fix and resend Use GET /v1/divisions.
segment_not_on_site 422 One or more segmentIds is not a segment on that user’s site. Returned identically whether it does not exist or belongs to another site. Nothing was changed. Fix and resend Use GET /v1/segments.
division_not_on_site 422 The divisionId you sent is not a division on that user’s site. Returned identically whether the division does not exist or belongs to another site. Fix and resend Use GET /v1/divisions for the divisions you can assign.
parent_not_on_site 422 The parentId you sent to PATCH /v1/divisions/{divisionId} is not a division on that division’s own site. Returned identically whether it does not exist or belongs to another site. Fix and resend Use GET /v1/divisions.
parent_is_self 422 You asked to make a division its own parent. Fix and resend Send another division’s id, or null to make it a root.
unsupported_field_for_service_type 422 The field cannot be written on this product’s service type. minQuantity and maxQuantity are readable everywhere but writable only on STOCK - on POD the bounds belong to the shared print-job classification, not the product. Fix and resend Refused rather than accepted upstream and then invisible here. Reading still works.
variant_not_site_addressable 422 This product’s variant cannot be changed for one site. Share-backed (SLS_) and print-on-demand products expose a variantId shared with every other site the same master product is shared to, so changing it would change it for them too. Not retryable Only site-owned (SLO_) products have a per-site variant. Product-level fields stay writable via PATCH /v1/products/{productId}. See Catalog and products.
rate_limit_exceeded 429 Your per-partner rate, burst, or daily quota. Carries Retry-After. Back off See Rate limits and quotas.
internal_error 500 An unexpected server-side failure. detail is always the fixed string An unexpected error occurred. and never echoes the underlying error. Retry once, well spaced. If it persists, escalate with the requestId.
service_unavailable 503 A transient failure in the service or the database it reads from. Back off Exponential, starting around one second.
audit_unavailable 503 This API records every write in its own audit trail before making it, and that record could not be written - so your change was not attempted. Back off Retry. Nothing was applied, so a retry is safe.

This is the complete set. One test in the service compares the OpenAPI contract to the codes the handlers actually construct, so a code cannot be added on one side and forgotten on the other; a second test compares this very table to that same contract, so the guide cannot drift back out of sync with it either. New codes can still appear inside v1 - treat one you do not recognize as “unhandled error for this status” rather than failing on it.

Both are produced by the API gateway, before your request reaches any application code: the 401 by the authorizer, the 429 by your usage plan. They are nonetheless shaped as the same problem document as everything else, with the same fields and a requestId, so you do not need a second code path for them.

The 401 is identical for every cause - no header, a malformed one, an expired token, a disabled credential - because a 401 that explained which check failed would help someone probing with guessed values more than it helps you. See when authentication fails.

The one response that is not a problem document

Section titled “The one response that is not a problem document”

The edge web application firewall enforces a per-IP rate-based rule and a set of managed rule sets. When either blocks a request the response is a 403 from the firewall: no problem document, and unrelated to your token’s scopes.

This is the one case in this API where a 403 does not mean “your credential lacks a capability”. If you are seeing 403 without an insufficient_scope body, look at your request rate and your source IP before you look at your scopes. See Rate limits and quotas.

A summary you can encode directly:

Status Retry? How
400 No The request must change. Retrying it unchanged returns the same answer forever.
401 Once Mint a fresh token, retry once. Then stop and investigate the credential.
403 No Either a missing scope (fix the token) or the edge firewall (slow down). Neither is fixed by an immediate retry.
404 No
409 No partner_order_id_reused means use a new partnerOrderId or resend the exact original body; ordering_not_provisioned means ask Partner Integrations.
422 No Fix the request per detail.
429 Yes Honor Retry-After, and add exponential backoff with jitter on top of it.
500 Cautiously Once, well spaced. A persistent 500 is a bug on our side and needs the requestId, not more traffic.
503 Yes Exponential backoff from around one second.

Cap your retries and add jitter. A fleet of integrations retrying a transient failure in lockstep turns a blip into an outage, and the 503 path exists precisely for moments when the service has less capacity than usual.

order_not_found means “no order matching this request is visible to you”. It does not distinguish “this order number was never issued” from “this order belongs to a site you are not granted”, and it never will - confirming existence is itself a disclosure. Do not write logic that treats a 404 from this API as grounds to mark a local record cancelled, deleted, or invalid. See Tenancy and access.

updatedSince is rejected with a 400 rather than accepted and ignored, because a filter you believe is working is how you end up trusting an incomplete feed. The updatedOn field is not a usable watermark either: it does not move when a shipment posts, a tracking number is added, or item quantities change on an existing order, and a separate internal process can move an order into your site without touching it.

GET /v1/orders ordered by placedOn discovers new orders and nothing else. What is missing is only the ability to ask which orders changed: fetching one order you already know about always returns current state, so re-requesting GET /v1/orders/{orderNumber}/shipments on your own schedule is the supported way to watch for tracking. See Tracking and line items.

A 404 on a sub-resource is about the order, not the collection

Section titled “A 404 on a sub-resource is about the order, not the collection”

GET /v1/orders/{orderNumber}/shipments and .../items return 200 with an empty array when the order resolves for you and has nothing to show yet. A 404 from either means the order number itself does not resolve for you. Do not collapse the two: the first means “not yet”, the second means “wrong order number, or not your site”.

Include the requestId from the failing response. It resolves to a single log entry, which is dramatically faster than a search by approximate timestamp. The only failure with no requestId to send is the edge 403, which never reaches our logs at all - for that one, include the exact time with a timezone, the host you called, and your source IP instead.