Skip to content

List every active product on your granted sites

GET
/products
curl --request GET \
--url 'https://api.partners.collaterate.com/v1/products?serviceType=STOCK&limit=100' \
--header 'Authorization: Bearer <token>'

The full ACTIVE catalog across your granted sites, one entry per variant, which is the unit an order line refers to.

This endpoint exists mainly so that ordering is possible at all: an order line must carry a productId and a variantId, and there is no other way to discover either. Neither the sku nor the productCode on an existing order line can be matched back to a catalog entry, so do not try – page this endpoint and key your own catalog on (productId, variantId).

Every ACTIVE product is listed, whatever its visibility. Products a site has hidden or restricted to particular customer segments appear with their visibility published as data (HIDDEN, VISIBLE_TO_SOME); retired and deleted products stay out. Whether an entry can actually be ordered is the orderable field, not the fact of being listed.

Migration note (2026-08-04). This endpoint used to list only orderable products and promised that an entry absent from the list would be rejected at submission. If you sync this catalog to drive ordering, filter on orderable === true to reproduce the old list byte for byte.

That old promise, however, was measured false when the submission path was traced on 2026-08-04 – it was never true for hidden or segment-restricted products, which submission accepts. orderable is therefore a statement about what this API supports, not a prediction of what the platform will refuse; see the field’s own description for exactly which conditions are enforced and which are not.

Prices are not published here, deliberately. What a product costs depends on variant adjustments, site markup and per-share pricing overrides, so any single column would be a base rate rather than what you pay. Submit the order and the response carries the real per-line and order totals.

Keyset-paginated, ordered by (productId, variantId). Follow nextCursor.

siteId
integer format: int32

Restrict the result to a single site. Must be one of the sites listed in your GET /v1/me response. A siteId outside your grant is rejected as a bad request – never silently ignored, and never answered with an empty page.

GET /v1/products?siteId=222 and GET /v1/sites/222/products are the same request – same handler, same result. A site outside your grant answers 400 invalid_site_id.

serviceType
string
Allowed values: STOCK POD

Restrict to one service type. Any other value is rejected rather than answered with an empty page, so that asking for a type this API cannot order is distinguishable from a site that happens to sell none of it.

cursor
string

Opaque pagination token from a previous response’s nextCursor. Do not parse it, construct it, or rely on its format staying the same. Pass it back as received.

limit
integer
default: 100 >= 1 <= 500

Page size. Defaults to 100. Values above 500 are capped at 500, not rejected.

q
string
>= 1 characters

Free-text search across one site’s catalog, ranked by relevance.

q requires siteId. Listing spans every site in your grant; searching does not. Relevance is computed per site and scores from different sites are not comparable, so a grant-wide search would return an arbitrary interleaving rather than a ranking. Pass siteId, or use GET /v1/sites/{siteId}/products?q=. Without one the request is 400 site_id_required_for_search.

q cannot be combined with cursor. A search returns a ranked result set, not a page, so there is nothing to resume from; nextCursor is always null. Use limit. Sending both is 400 cursor_not_supported_with_search.

With q, limit counts matched products rather than returned entries. A product is ranked once and published as one entry per variant, so a search for 20 products returns every variant of those 20. serviceType narrows a search exactly as it narrows a listing.

Search reaches slightly less than the listing. Products a site has marked HIDDEN, and products flagged to be excluded from search, are not searchable, though the listing still returns them with their visibility published. A product created moments ago may take a little while to become searchable. Results are also relevance-filtered, so a search can return fewer products than limit even when the catalog holds more.

Relevance scores are not published. They order the results and are deliberately not part of this contract.

Where search is not available in an environment, q answers 400 search_not_available. A failure of the search service answers 503, never an empty result set: an empty products array always means “nothing matched”.

A page of catalog entries.

Media typeapplication/json
object
products
required
Array<object>

One catalog variant. (productId, variantId) identifies it, and both are required on the order line that buys it – but check orderable first: since 2026-08-04 this catalog lists every ACTIVE product, not only the orderable slice.

object
siteId
required

The granted site this product belongs to.

integer
productId
required

The identifier an order line must carry. Prefixed by origin: SLO_ for a product the site owns, SLS_ for one shared to it. Treat it as an opaque string – the prefix is informative, not something to branch on.

string
variantId
required

The specific variant, or null for a POD product, which has no variant until the job is configured. A STOCK product appears here once per variant.

Send this back exactly as received, null included. An order line whose variantId does not resolve is rejected, and a null is accepted – so do not substitute a placeholder for a POD product.

integer | null
sku
required

null for a POD product, which has no SKU.

string | null
name
required
string
description
required

For an SLS_ product this is the value the storefront shows: the SHARE’s own copy when it overrides the master system offering’s display fields, otherwise the MASTER’s. Resolved by this API rather than read straight from the catalog view, which applies that rule to a warehoused share and not to a print-on-demand one.

string | null
serviceType
required

STOCK is warehoused inventory; POD is print-on-demand and is quoted per order; DIGITAL is listed (it is catalog) but cannot be ordered through this API, so it is never orderable. The serviceType query filter still accepts only the two orderable types.

string
Allowed values: STOCK POD DIGITAL
availableInventory
required

Stock on hand, or null when inventoryTracked is false – meaning no quantity can be stated for this product, NOT that it has none. Always null for POD, which is produced per order.

A product is listed whatever this says – zero does not remove it, because a product that vanished at zero would be indistinguishable from one that was discontinued. Whether a shortfall blocks an order is decided when the order is submitted, from the site’s backorder setting.

Before 2026-07-29 an untracked product reported 0 here, which read identically to genuinely out-of-stock; one variant was hiding 2,065 units. If you cached the old value, treat 0 as unknown rather than empty.

integer | null
inventoryTracked
required

Whether availableInventory means anything. When false, this product does not track inventory: no quantity is published, and only submission can tell you whether a given quantity will be accepted.

boolean
minQuantity
required

Minimum orderable quantity, or null for no minimum.

integer | null
maxQuantity
required

Maximum orderable quantity, or null for no maximum.

integer | null
shippable
required
boolean
weight
required

Per-unit weight as a decimal string, or null for a POD product, which has none until the job is configured. A string for the same reason order totals are: the underlying value carries more precision than a JSON number holds.

string | null
updatedOn
required

When the catalog entry last changed. Informational – there is no updatedSince filter on this endpoint, so do not build an incremental sync on it.

string format: date-time
artworkSource

How this POD product’s artwork is supplied. null for STOCK (always) and for a small fraction of POD products with no classification on file – treat null as “not classified,” not “no artwork needed.”

  • USER_UPLOAD: send a file at order time (POST /v1/orders’s artworkUrl).
  • STORED: artwork is already on file; no submission-time artwork data needed.
  • TEMPLATE: a design template with fields to fill in. Call GET /v1/products/{productId}/template for the vendor, template id, and the variable keys to send back as templateVariables.
  • PLUGIN: driven by a storefront plugin outside this API’s model.
string | null
Allowed values: USER_UPLOAD STORED TEMPLATE PLUGIN
productStatus
required

Always ACTIVE today – only ACTIVE products are listed, and retired or deleted ones stay out. Published so that scope is stated rather than implied.

string
visibility
required

Who can see this product on its own storefront, by name (the raw visibility id is never published). VISIBLE_TO_SOME means segment-gated: visible only to customers in one of the product’s segments. Feeds orderable below.

string
Allowed values: HIDDEN VISIBLE_TO_ALL VISIBLE_TO_SOME
orderable
required

Whether an order line for this entry is promised to be accepted: true exactly when the product has a fulfillment center, is STOCK or POD, and is VISIBLE_TO_ALL – the same gate that decided, before 2026-08-04, whether the entry was listed at all, so filtering on orderable === true reproduces the old list.

false means not supported and not promised, which is not the same as “rejected”. Measured against the submission path on 2026-08-04: an order line is genuinely rejected for a product with no fulfillment center, for a DIGITAL product, and for a retired SLS_ product. It is not rejected for a HIDDEN or VISIBLE_TO_SOME product – submission applies no visibility or segment check, so such a line is currently accepted, priced and fulfilled even though this API does not support ordering it. Do not rely on the platform to enforce what orderable: false describes.

boolean
namePlural
required

The plural display name, when the site has set one.

string | null
excerpt
required

Short storefront blurb, when set.

string | null
metaDescription
required

SEO meta description, when set. For an SLS_ product this is the master system offering’s unless the share overrides the master’s display fields – the same inheritance description documents, and the same values the storefront renders.

string | null
metaKeywords
required

SEO meta keywords, when set. Inherited from the master system offering on the same rule as metaDescription.

string | null
urlPattern
required

The storefront URL slug, when set.

string | null
excludeFromSearch
required

Whether the site excludes this product from storefront search.

boolean
imageUrl
required

Fully-qualified URL of the product’s PRIMARY image, fetchable as-is. The host is the product’s own site, so two products on different sites return different hosts.

null when the product has no image. The gallery is a separate sub-resource – GET /v1/products/{productId}/images.

string | null
thumbnailUrl
required

Fully-qualified URL of the primary image’s thumbnail, or null.

string | null
attributes
required

This variant’s attribute pairs (e.g. {"name": "Color", "choice": "White"}), in attribute order. Only complete pairs are published – a title without a choice, or the reverse, is omitted – and a product with no attributes carries [].

Array<object>

One attribute pair of a variant. Both sides are always present – incomplete pairs are not published.

object
name
required

The attribute’s name, e.g. Color.

string
choice
required

This variant’s choice for it, e.g. White.

string
actualInventory
required

On-hand stock before reservations and backorders, or null whenever inventoryTracked is false – all four inventory fields are null together, for the reason given on availableInventory.

integer | null
reservedInventory
required

Stock reserved by open orders, or null when untracked. See actualInventory.

integer | null
backorderInventory
required

Quantity on backorder, or null when untracked. See actualInventory.

integer | null
defaultQuantity
required

The quantity the storefront pre-fills, when one is set.

Null for every STOCK product, always – there is no such setting on a stock product upstream, so this is not a gap in your data. On POD products it is derived from the pricing configuration, which this API does not expose. It is read-only and cannot be written through PATCH /v1/products/{productId}.

integer | null
stockUnitOfMeasure
required

The unit availableInventory and friends count in (e.g. EA), when set.

string | null
overrideDisplay
required

Whether a share-backed (SLS_) product overrides the master product’s display fields, or null on a site-owned (SLO_) product, which has no master and no override mechanism at all.

One flag covers several fields. false means description, metaDescription, metaKeywords and the thumbnail fields are all INHERITED from the master, and the values published here are the inherited ones – the same values the storefront shows. true means this share’s own values stand. There is no per-field inheritance, and writing one of those fields does not change the flag.

boolean | null
requiresApproval
required

Whether the site requires an approval step before this product’s orders proceed.

boolean
supplierReference
required

The site’s own supplier reference for this product, when set.

string | null
fulfillmentCenterName
required

The fulfillment center this product ships from, by name, or null when none is assigned – in which case the product is never orderable.

string | null
nextCursor
required

Opaque cursor for the next page, or null on the last page. Terminate your loop on null specifically.

string | null
Examples
Exampledefault
{
"products": [
{
"siteId": 42,
"productId": "SLS_918273",
"variantId": 55012,
"sku": "BC-100-WHT",
"name": "Business Card",
"namePlural": "Business Cards",
"description": "16pt uncoated, full color both sides",
"excerpt": null,
"metaDescription": null,
"metaKeywords": null,
"urlPattern": "business-cards",
"excludeFromSearch": false,
"imageUrl": "https://acme.collaterate.com/resources/offering_images/business-cards.png",
"thumbnailUrl": "https://acme.collaterate.com/resources/offering_images/business-cards-thumb.png",
"serviceType": "STOCK",
"productStatus": "ACTIVE",
"visibility": "VISIBLE_TO_ALL",
"orderable": true,
"attributes": [
{
"name": "Color",
"choice": "White"
}
],
"availableInventory": 1450,
"actualInventory": 1500,
"reservedInventory": 50,
"backorderInventory": 0,
"inventoryTracked": true,
"minQuantity": null,
"maxQuantity": null,
"defaultQuantity": 100,
"stockUnitOfMeasure": "EA",
"requiresApproval": false,
"supplierReference": null,
"fulfillmentCenterName": "Cypress",
"shippable": true,
"weight": "0.0120000000",
"updatedOn": "2026-07-14T09:31:00.000Z"
},
{
"siteId": 42,
"productId": "SLS_1",
"variantId": null,
"sku": null,
"name": "Announcement",
"namePlural": "Announcements",
"description": "Digitally printed, ordered to exact quantity",
"excerpt": null,
"metaDescription": null,
"metaKeywords": null,
"urlPattern": "announcements",
"excludeFromSearch": false,
"imageUrl": null,
"thumbnailUrl": null,
"serviceType": "POD",
"productStatus": "ACTIVE",
"visibility": "VISIBLE_TO_SOME",
"orderable": false,
"attributes": [],
"availableInventory": null,
"actualInventory": null,
"reservedInventory": null,
"backorderInventory": null,
"inventoryTracked": false,
"minQuantity": 50,
"maxQuantity": 5000,
"defaultQuantity": null,
"stockUnitOfMeasure": null,
"requiresApproval": false,
"supplierReference": null,
"fulfillmentCenterName": "Cypress",
"shippable": true,
"weight": null,
"updatedOn": "2024-08-01T07:32:35.125Z"
}
],
"nextCursor": "eyJwIjoiU0xTXzkxODI3MyIsInYiOjU1MDEyfQ"
}

A cursor that could not be decoded, a siteId that is not a positive integer or is outside your granted sites, a serviceType this API cannot order, or any other query parameter this endpoint does not accept (unknown_query_parameter).

For q specifically: an empty or whitespace-only value (invalid_query), q without siteId (site_id_required_for_search), q together with cursor (cursor_not_supported_with_search), or q in an environment where search is not available (search_not_available).

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_service_type",
"detail": "\"serviceType\" must be one of STOCK, POD, got: DIGITAL. Only these two are orderable through this API.",
"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-..."
}

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-..."
}