Skip to content

Pricing

The catalog deliberately publishes no prices. Two endpoints are where a real number comes from before an order exists:

  • POST /v1/products/{productId}/quote prices the goods - the same pricing the storefront checkout computes, for one product at one quantity.
  • POST /v1/shipping/quote prices getting them to an address - one price per shipping service the site offers for that destination.

Both take partner-api/products:read. A quote is a read that happens to be a POST, so there is no separate scope for either, and no shipping:read.

This page covers both requests, what each response field means, and the limits every integrator eventually runs into - the discount a product quote cannot see, the precision it does not carry, and the one number the shipping quote will not compute for you.

The productId comes from the catalog - either a GET /v1/products entry or the productId on an existing order line. The endpoint needs partner-api/products:read, the same scope as the rest of the product endpoints; a 403 insufficient_scope here means the grant is missing, not that the product is off-limits.

The body carries exactly one field:

Terminal window
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "quantity": 250 }' \
https://api.partners.collaterate.com/v1/products/SLS_918273/quote

quantity is required and must be a positive integer. There is nothing else to send - no siteId, in particular. Every SLO_/SLS_ id belongs to exactly one site, so the site is resolved from the productId itself.

{ "productId": "SLS_918273", "quantity": 250, "totalPrice": 89.99, "createdOn": "2026-07-30T00:00:00.000Z" }

Four fields, all always present:

Field What it is
productId The product you asked about, echoed back.
quantity The quantity you asked about, echoed back.
totalPrice The price for this quantity - the whole-quantity total, not a per-unit price. Post site-markup, post any product-level discount the site has configured. A JSON number; see the caution below.
createdOn When this quote was computed.

createdOn is worth reading for what it is not: a quote is a point-in-time answer, not a reservation and not a locked price. Nothing about a later submission is bound to it - there is no quote id to send with an order, and Collaterate prices every order fresh from its own catalog and site rules at submission time (which is also why a submission carries no price field at all). A quote that has been sitting in your cache is a record of what the price was, and the only way to learn the current one is to quote again.

This is the site’s price, not a specific customer’s price

Section titled “This is the site’s price, not a specific customer’s price”

Collaterate applies two independent things when it prices a product: the site’s own pricing (a markup and, per product, an optional discount) and, separately, a specific customer’s own negotiated account discount. This endpoint has no logged-in customer to attach to a quote request, so it only ever reflects the first - it does not, and cannot, reflect what a specific already-discounted customer would actually pay. If you’re quoting on behalf of a customer who has their own negotiated pricing, this number will read higher than what they’d really be charged at checkout.

The product quote tells you what the goods cost. POST /v1/shipping/quote tells you what it costs to get them somewhere:

Terminal window
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"siteId": 42,
"shipTo": { "country": "US", "state": "MN", "postalCode": "55416" },
"weightOunces": 37.5
}' \
https://api.partners.collaterate.com/v1/shipping/quote
Field Required What it is
siteId yes Which of your granted sites to price against.
shipTo.country yes Two-letter country code - the same vocabulary you send on POST /v1/orders and read back on shipments.
shipTo.state yes State or province abbreviation as spelled within that country.
shipTo.postalCode yes Destination postal code, passed to the carrier as given.
weightOunces yes Total shipment weight in ounces. See below - this one is on you.
productId no Selects the fulfilment centre the shipment would ship from, making the quote origin-correct. Must be a product on siteId.

siteId is required here even though the product quote needs none, and the difference is real: an SLO_/SLS_ id names exactly one site, but shipping is priced from site configuration - which carriers are enabled, whether negotiated rates apply, the site’s own custom shipping options - and productId is optional. With two granted sites and nothing to resolve from, the alternative would be quoting against whichever one we picked.

There is no shipTo.name, address1 or city. Carriers rate on destination and weight; a quote needs no recipient, so this endpoint does not ask for one. Unknown keys are refused by name rather than ignored, at both levels, so sending a full order-shaped shipTo tells you which parts do not apply instead of quietly discarding them.

state is looked up inside the country you sent, because state abbreviations are not unique across countries: BC is British Columbia under CA and Baja California under MX. Get the pair wrong and you get 422 unknown_state_code, never a quote for the wrong continent.

This API will not compute the weight for you, and that is deliberate.

GET /v1/products publishes a per-unit weight for every variant, so the obvious thing is for us to multiply it by a quantity and convert. We will not, for one reason: the platform records no unit. There is no unit column, no per-site setting, and no documented convention - the API reference says as much in as many words, describing weight as being “in the catalog’s own unit”. Measured across 142,967 active variants the values run from 0 to 8,645 with a mean of 4.15, which is consistent with ounces, with pounds, and with kilograms.

Converting an unknown unit to ounces means inventing a conversion factor. The failure mode is not an error you would see - it is a plausible-looking shipping price that is wrong by a constant multiple on every quote, which neither you nor we could detect from the response. So the endpoint asks, because we genuinely do not know.

You are in a better position than we are. If you know what unit your own products are kept in, weight x quantity, converted once by you, is exactly the number to send - and it is right.

weightOunces is a decimal, not an integer (half an ounce is a real parcel weight), must be greater than zero, and is capped at 5,000,000 as a guard against a transposed decimal point. That cap is not a carrier limit; carriers refuse what they refuse and tell you so.

{
"quotes": [
{ "serviceName": "UPS Ground", "price": "12.45" },
{ "serviceName": "UPS 2nd Day Air", "price": "31.80" }
],
"warnings": [],
"messages": []
}
Field What it is
quotes[].serviceName The carrier service as Collaterate names it - what to show a customer.
quotes[].price The price for that service, as a decimal string. Unlike the product quote’s totalPrice, this follows the API-wide money rule.
warnings Collaterate’s reasons that some option could not be priced. Non-empty here means a partial result - typically one carrier failing while others answered.
messages Informational notes intended for display. Never errors.

Both message lists are display text, not codes. Show them; do not branch on them.

quotes is never empty on a 200. Upstream, a destination nothing can be shipped to comes back as a perfectly ordinary success with an empty list and the reason buried in a side channel - which reads exactly like “everything is fine and there is nothing to ship with”. This API does not relay that shape: no priced service at all is a 422 shipping_quote_unavailable with Collaterate’s own reasons in detail. You never have to tell an empty array apart from a real answer.

Collaterate’s own service ids and its “carrier service or site custom option” flag are not published. The id is an internal key that nothing in this API accepts back - POST /v1/orders carries no service selection - and the flag describes how Collaterate is configured rather than what you are buying.

Status Code Meaning
400 invalid_product_id The productId path segment doesn’t match SLO_/SLS_ plus digits.
400 invalid_quantity quantity is missing or not a positive integer.
403 insufficient_scope Your token lacks partner-api/products:read - ask Partner Integrations to add it.
404 product_not_found No such product, or it exists outside your granted sites - these read identically on purpose, see Tenancy and access.
404 quote_product_not_found Rare: the product resolved in our catalog but Collaterate could not price it.
422 quote_invalid Collaterate rejected the quote on business-validation grounds (e.g. quantity over the product’s configured maximum). detail carries Collaterate’s own message.

The 422 deserves a sentence beyond its table row, because it is the one you will actually see: products carry quantity rules configured in Collaterate - a maximum is the common case - and the catalog does not publish them, so this API cannot check your quantity against them up front. Asking for more than the product allows comes back as quote_invalid with Collaterate’s own validation message in detail; the fix is to change the request (reduce the quantity), not to retry it.

Status Code Meaning
400 invalid_shipping_quote_request The body failed validation - malformed JSON, a missing or mistyped field, an unknown key, or a weightOunces outside 0 < w <= 5000000. detail names the field.
400 invalid_site_id siteId is not one of your granted sites. Check GET /v1/me.
403 insufficient_scope Your token lacks partner-api/products:read.
404 product_not_found The productId does not exist, is outside your granted sites, or belongs to a different site than the siteId you sent. All three read identically.
422 unknown_country_code shipTo.country is not a country code Collaterate recognises.
422 unknown_state_code shipTo.state is not a state or province of the country you sent.
422 shipping_quote_unavailable Nothing could be priced for that destination and weight. detail carries Collaterate’s own reasons - an invalid postal code for the country is the common one.

Both endpoints also return the API-wide 401, 429, 500 and 503 responses, which behave the same here as everywhere else - see Errors for the full catalog.

  • Catalog and products - where the productId you quote comes from, and the per-unit weight you convert into weightOunces.
  • Submitting orders - turning the quoted product into an order, and the receipt that comes back.
  • Errors - the full code catalog, including the API-wide responses above.