Prices an order and returns a signed quote_token (24h TTL). Pass the token to POST /order to charge exactly the quoted total — no re-pricing surprise. May include a non-binding delivery_risk advisory and cross-border presentment/duties blocks.
- Credential
- An API key in
x-api-key - Scopes
pricing:read
Parameters
Idempotency-Keyheader · string (uuid)Unique key for idempotent requests. If you retry a request with the same key within 24 hours, you receive the original response. The key is bound to the request payload: reusing it for a different payload returns 422
idempotency_key_reuserather than the original response, so a changed request is never silently dropped. The binding is over the JSON's content, not its bytes — keys in another order or other whitespace are the same request. Generate a new key for each new request. On an operation that moves money (an order, a batch, an item, a shipping or address change, a cancellation's refund), a key that cannot be checked because the idempotency ledger is unreachable is answered 503idempotency_unavailablewithRetry-After, and nothing runs or is charged: retry with the same key.
Request body application/json, required
itemsobject[]requiredAt most 100 lines of at most 10,000 units each, the bounds POST /order takes; a cart beyond either is 400 validation_error, as the order would refuse it.
1 to 100 entries
items[].skustringrequirednon-empty
items[].quantityintegerrequired1 to 10000
items[].variable_dataobjectThe same per-unit field POST /order takes, preflighted the same way, so an overflow or a count mismatch is refused here before you order. Never priced: the quote is what it is without it. The response's
variable_datacarries the variant proofs.items[].variable_data.slotsobject[]requiredThe named text slots of the line's design, each with the most characters it holds.
1 to 10 entries
items[].variable_data.slots[].namestringrequiredLower-case letters, digits and underscores, starting with a letter, e.g. name or serial. Each name once.
matches ^[a-z][a-z0-9_]{0,39}$
items[].variable_data.slots[].max_lengthintegerrequiredThe longest value this slot prints. A longer value is refused as
overflow, not shrunk or cut.1 to 200
items[].variable_data.unitsobject[]One object per unit, in print order: { slot name: value }. Its length must equal the line's quantity. Values are strings or numbers, with no line breaks. Omit it when a
sequencefills every slot.1 to 10000 entries
items[].variable_data.sequenceobjectNumbers one slot per unit: prefix + (start + (unit - 1) × step), zero-padded to
paddigits, e.g. A-0001, A-0002. A unit may not also set that slot.items[].variable_data.sequence.slotstringrequiredA declared slot.
non-empty · at most 40 characters
items[].variable_data.sequence.prefixstringPrinted before the number. Default empty.
at most 40 characters
items[].variable_data.sequence.startintegerThe first unit's number. Default 1.
0 to 1000000000
items[].variable_data.sequence.stepintegerAdded per unit. Default 1.
1 to 1000000
items[].variable_data.sequence.padintegerZero-pad the number to this many digits. Default 0 (no padding).
0 to 12
items[].variable_data.qrobjectEach unit's QR payload, built from its values.
items[].variable_data.qr.templatestringrequiredText with {slot} placeholders, e.g. https://example.com/t/{serial}. Every placeholder must name a declared slot.
non-empty · at most 1000 characters
items[].variable_data.qr.max_lengthintegerThe longest payload accepted, for a small sticker whose code must stay scannable. Default 1000.
1 to 1000
shippingobjectshipping.methodstringstandard (default); priority or express once they are priced — until then a request for either is 400 shipping_method_unavailable. Any other value is 400 validation_error, as on POST /order.
One of
standardpriorityexpresslettercurrencystringOptional presentment currency (ISO 4217); adds a presentment block when non-USD. The block's amount_cents is in that currency's own minor units and minor_units carries the exponent (0 for JPY, 2 for EUR). An unsupported currency is rejected with unsupported_currency rather than quoted at a fallback rate.
exactly 3 characters
destination_countrystringISO 3166-1 alpha-2. Our own plant ships to US addresses only, so any other country is refused 422 destination_unavailable.
address_to.country, when sent, is the one checked.exactly 2 characters
deadlinestring (date-time)Arrival deadline — delivery_risk warns when it's tight.
deliver_bystring (date)A day the order should arrive by, YYYY-MM-DD (UTC). Answered in
deliver_by_check, judged against the estimate's latest day rather than its earliest, from the facility's production estimate and the method's transit window. Optional; a day before today, or anything but a real date, is 400 validation_error.providerstringThe lane to price: always our own plant here, which is where POST /order makes the order. Omit it, or send
printedsimply; any other value is refused with 422provider_not_availablebefore anything is priced.Always
printedsimplyDefault printedsimply
address_toobjectThe destination. Optional: our own plant prices shipping from its own table, so only its country is read, and it ships to US addresses only (any other country is refused 422 destination_unavailable).
address_to.namestringrequirednon-empty
address_to.companystringaddress_to.address1stringrequirednon-empty
address_to.address2stringaddress_to.citystringrequirednon-empty
address_to.statestringrequirednon-empty
address_to.zipstringrequirednon-empty
address_to.countrystringrequiredISO 3166-1 alpha-2 country code
exactly 2 characters
address_to.phonestringaddress_to.emailstring (email)
Request
POST /api/v1/orders/quote
curl -X POST "https://printedsimply.com/api/v1/orders/quote" \
-H "x-api-key: ps_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quote-order-$(date +%s)" \
-d '{
"items": [
{
"sku": "sticker-die-cut-3x3",
"quantity": 100
}
],
"shipping": {
"method": "standard"
}
}'Response 200
Signed quote, priced at our own plant: quote_id, quote_token (pass it to POST /order to be charged exactly this total), expires_at, lines, breakdown, margin and shipping (its method, and the peak-season surcharge the shipping figure already holds, itemised); plus estimated_delivery, deliver_by_check when the request named a deliver_by day, delivery_risk when the order carries a known risk, dispatch_promise (switched off today), presentment when a currency other than USD was asked for, variable_data when a line carries it, and, on a test key, pricing_degraded.
data
fulfillment_policyobjectOptional exact-five approved fulfillment disclosure. Applies only to the four listed double-sided flags and one Santa hat, each a separate quantity-1 US standard order. Actual manufacturing expense and contribution remain unknown; the shipping benchmark includes packing. Supplies no live mapping by itself.
fulfillment_policy.fulfillment_cost_basisstringrequiredApproved per-size US standard shipping benchmark; actual postage, manufacturing cost and profit remain unknown.
Always
approved_size_shipping_benchmarkfulfillment_policy.goods_cost_centsanyrequiredActual manufacturing cost is unknown, never represented by zero.
fulfillment_policy.manufacturing_cost_centsanyrequiredfulfillment_policy.actual_contribution_centsanyrequiredfulfillment_policy.maximum_quantityintegerrequiredOne product, one SKU, quantity 1 in its own order.
Always
1fulfillment_policy.shipping_constraintsobjectrequiredfulfillment_policy.shipping_constraints.destination_countrystringrequiredAlways
USfulfillment_policy.shipping_constraints.methodstringrequiredAlways
standardfulfillment_policy.shipping_constraints.separate_orderbooleanrequiredAlways
truefulfillment_policy.shipping_benchmark_centsintegerrequiredThe approved quantity-1 US standard benchmark, packing included.
at least 0
fulfillment_policy.packagingstringrequiredAlways
included_in_shipping_benchmarkfulfillment_policy.artwork_layoutstringrequiredAlways
prepared_combined_allfulfillment_policy.artwork_instructionsstringrequiredPrepare one combined ALL PNG: flag front and back, or Santa hat front and band, according to the exact guide; remove guide lines.
fulfillment_policy.artwork_guide_urlstringrequiredLocal path to the layout guide SVG. This reference is not customer artwork and must not be sent as print_file.
quote_idstringrequiredaddress_checkobjectWhat POST /addresses/validate finds in
address_to, when the quote was sent one. A live key is refused an address with anerrorfinding (422 address_invalid); on a test keyvalid: falsehere says a live quote would have been.address_check.validbooleanrequiredFalse when any finding is an
error.address_check.checkedbooleanrequiredFalse for an address outside the US, which is passed through as written.
address_check.normalizedobjectrequiredThe address in USPS style: upper case, no punctuation, ZIP+4 hyphenated, the state as its code, a unit typed into address1 moved to address2. Name, company, phone and email are trimmed and otherwise as sent.
address_check.normalized.namestringaddress_check.normalized.companystringaddress_check.normalized.address1stringrequiredaddress_check.normalized.address2stringaddress_check.normalized.citystringrequiredaddress_check.normalized.statestringrequiredaddress_check.normalized.zipstringrequiredaddress_check.normalized.countrystringrequiredaddress_check.normalized.phonestringaddress_check.normalized.emailstringaddress_check.findingsobject[]requiredaddress_check.findings[].codestringrequiredThe
delivery_point_*codes come from the configured USPS or ShipStation service on live address-validation, quote, order, bulk and address-update requests.delivery_point_not_found(error) is a definite invalid-address verdict;delivery_point_incomplete(warning) means the unit or full address could not be confirmed;delivery_point_confirmed(info) means this address was confirmed. Test keys never call the service. Unconfigured or unavailable services leave only the local ZIP/state findings, without claiming street confirmation.One of
zip_formatstate_unknownzip_state_mismatchzip_state_borderzip_unassignedpo_boxmilitary_statemilitaryaddress2_splitnot_checkeddelivery_point_not_founddelivery_point_incompletedelivery_point_confirmedaddress_check.findings[].severitystringrequirederror: the address cannot be right as written, and a live order or quote is refused with 422 address_invalid.warning: worth a look, never refused.info: what normalization did, or that nothing was checked.One of
errorwarninginfoaddress_check.findings[].fieldstringrequiredOne of
address1address2citystatezipcountryaddress_check.findings[].messagestringrequiredquote_tokenstringrequiredSigned, and what
POST /v1/orderhonours. It carries the quote's own numbers, so an order placed against it cannot be re-pointed at a cheaper one.expires_atstring (date-time)requiredAfter this the token is refused and the quote must be taken again.
linesobject[]requiredThe priced lines, each { sku, quantity, unit_cents, line_total_cents }.
lines[].skustringrequiredlines[].quantityintegerrequiredlines[].unit_centsintegerrequiredWhat this API charges per unit, in USD cents. Where a price is a pack total that does not divide into whole cents a unit, this is the SKU's total over its quantity on the quote, rounded to the cent, for display, and the lines of one SKU share it: it is not necessarily this line's
line_total_centsoverquantity. Read each line's money fromline_total_cents, never as this timesquantity.lines[].line_total_centsintegerrequiredWhat an order placed against this quote charges for this line's goods, exact, in USD cents; the lines sum to
breakdown.items_cents. Equal tounit_cents×quantityon every line priced in whole cents a unit.breakdownobjectrequiredbreakdown.items_centsintegerrequiredbreakdown.shipping_centsintegerrequiredbreakdown.processing_fee_centsintegerrequiredCompatibility field for platform and card-processing surcharges in cents. Zero on orders priced under the current no-added-fees policy; retained on historical records.
breakdown.platform_fee_centsintegerThe platform fee as a line of its own. Always 0: there is no platform fee on any order, on any plan (since 2026-10-17). Kept so a client that reads it still parses.
breakdown.card_fee_centsintegerCard processing on everything else the order is charged, passed through at cost.
breakdown.total_centsintegerrequiredThe total before sales tax.
breakdown.tax_centsintegerThe sales tax on the goods and shipping for
address_to, calculated by Stripe Tax and signed intoquote_token. Present only whentax.statusisquoted.breakdown.total_with_tax_centsintegerWhat an order placed against this quote is charged:
total_cents+tax_cents. Present only withtax_cents.taxobjectWhether sales tax is in this quote. Present only while sales tax collection is on, for a live key, on a lane we charge for; absent otherwise, and then an order against the quote carries no tax.
quoted: the tax is inbreakdown.tax_centsand inside the signature, and the order must ship to the quotedaddress_to(another address is refused 400 quote_mismatch,details.reason: tax_address_changed).at_order: the quote had noaddress_to, so the tax is calculated when the order is placed and added to its charge. While tax collection is on, an order refuses a quote signed without this block (400 quote_mismatch,details.reason: tax_not_quoted): request a fresh one.tax.statusstringrequiredOne of
quotedat_ordertax.tax_centsintegerOn
quotedonly: the same figure asbreakdown.tax_cents.tax.notestringrequiredmarginobjectrequiredWhat to sell it for and what that leaves. A suggestion, not a commitment.
margin.suggested_retail_centsintegerrequiredmargin.margin_pctnumberrequiredpresentmentobjectThe total in the buyer's currency. Present only on a cross-border quote.
dutiesobjectPresent only when duties apply.
landed_costobjectlanded_cost.destination_countrystringrequiredlanded_cost.duty_basisstringrequiredWho pays at the border. The VALUE is the incoterm; what customs assesses on is goods plus shipping plus duty, which is a different fact and was the whole of this description before.
One of
DDPDAPlanded_cost.duty_ratenumberrequiredlanded_cost.tax_ratenumberrequiredlanded_cost.breakdownobjectrequiredlanded_cost.breakdown.goods_centsintegerrequiredlanded_cost.breakdown.shipping_centsintegerrequiredlanded_cost.breakdown.duties_centsintegerrequiredlanded_cost.breakdown.tax_centsintegerrequiredlanded_cost.total_landed_centsintegerrequiredlanded_cost.pre_collected_centsintegerrequiredCharged with the order.
landed_cost.due_on_delivery_centsintegerrequiredWhat the courier bills the recipient. The number a buyer is surprised by if nobody told them.
landed_cost.presentmentobjectrequiredThe same total in the destination's currency.
landed_cost.presentment.currencystringrequiredlanded_cost.presentment.amount_centsintegerrequiredlanded_cost.presentment.minor_unitsintegerrequiredlanded_cost.presentment.fx_ratenumberrequiredlanded_cost.estimatedbooleanrequiredAlways true off this path, and sent anyway: a consumer must be able to tell an estimate from a settled charge without knowing which endpoint produced it.
landed_cost.notestringrequiredguaranteeobjectThe delivery guarantee this quote would carry. Present only when one is offered on the lane.
dispatch_promiseobjectA dispatch-by date for an order made at our own plant, and what is paid if it leaves the plant later. Present only when the platform has switched the dispatch promise on AND every line's measured dispatch p95 is inside its window — so today, with the promise switched off, no quote carries it. Inside the quote signature; an order placed against this quote is held to exactly this date and remedy.
dispatch_promise.dispatch_bystring (date-time)requiredThe order is promised to leave the plant by this instant.
dispatch_promise.business_daysintegerrequiredThe window the date was computed with, in business days (Saturday and Sunday do not count).
dispatch_promise.remedyobjectrequireddispatch_promise.remedy.typestringrequiredshipping: the order's shipping charge is refunded.credit: a fixed amount is refunded.One of
shippingcreditdispatch_promise.remedy.centsintegerrequiredWhat a miss refunds, capped by what the order's charges have left.
delivery_riskobjectPresent only when the lane carries a known risk worth stating before the order. Non-binding. When a carrier advisory touches the destination between today and the estimate's latest day,
at_riskis true andadvisorieslists them; a quote may also carry its lane's lead-time read beside them.delivery_risk.at_riskbooleanPresent, and true, when
advisoriesis.Always
truedelivery_risk.advisoriesobject[]The carrier advisories that touch the destination while the parcel would be on its way.
delivery_risk.advisories[].idstringrequiredadv_….delivery_risk.advisories[].carrierstringrequiredThe carrier that posted it, as it names itself, e.g. "USPS".
delivery_risk.advisories[].statesstring[]requiredUSPS state codes it touches. With
zip_prefixesboth empty, it touches every US destination.delivery_risk.advisories[].zip_prefixesstring[]requiredZIP prefixes (one to five digits) it touches, beside
states.delivery_risk.advisories[].fromstring (date)requiredThe first day it applies, YYYY-MM-DD in UTC, inclusive.
delivery_risk.advisories[].throughstring (date)requiredThe last day it applies, YYYY-MM-DD in UTC, inclusive.
delivery_risk.advisories[].activebooleanrequiredWhether today (UTC) is inside
from–through.delivery_risk.advisories[].extra_daysintegerrequiredThe carrier's estimate of the business days it adds, as posted. An estimate.
delivery_risk.advisories[].messagestringrequiredOne sentence about it.
delivery_risk.advisories[].sourcestringrequiredThe carrier's own notice, as a URL.
estimated_deliveryobject | nullWhen an order placed now should arrive, as an estimate: the facility's production estimate (at our plant, its slowest line's: a wall decal of our catalog takes the 3 days its owner's sheet states, everything else the plant's own estimate) plus the shipping method's transit window (our plant's standard parcel from the plant's own transit estimate), in business days counted in UTC, Monday to Friday, never on a day GET /calendar lists (the federal holidays, each on the weekday it is observed, and the plant's own closure days). Null for the exact-five approved fulfillment policy: production/transit dates are not established by a shipping benchmark. Not a promise and not inside
quote_token: the binding date, where the account has one, isdispatch_promise. Present on a quote priced at one of our own facilities; absent on a degraded quote and on any other lane, which sets its own times.estimated_delivery.earlieststring (date)requiredYYYY-MM-DD, UTC.
estimated_delivery.lateststring (date)requiredYYYY-MM-DD, UTC.
estimated_delivery.ships_bystring (date)requiredYYYY-MM-DD, UTC: the day an order placed now should leave the facility, when its production estimate ends, counted the same way.
estimated_delivery.is_estimatebooleanrequiredAlways true: say it as an estimate wherever it is shown.
Always
truedeliver_by_checkobjectThe answer to
deliver_by, read as an estimate: production and transit are the facility's and the carrier's estimates, so a day thatmeetscan still be missed. Present only whendeliver_bywas sent and there is an estimate to judge it against.deliver_by_check.requestedstring (date)requiredThe day asked about, as sent.
deliver_by_check.meetsbooleanrequiredOn a quote: whether its own method's estimate ends on or before
requested(estimated_delivery.latest, never the earliest day). On a shipping estimate: whether any parcel method listed does. False when a carrier advisory touches the destination (statusisat_risk): true exactly whenstatusismeets.deliver_by_check.earliest_methodstring | anyrequiredThe parcel method on offer whose estimate ends soonest, when it ends on or before
requested; null when no method on offer is estimated to arrive by then. Neverletter.deliver_by_check.latest_order_daystring (date) | nullrequiredThe last UTC day an order can be placed and still be estimated to arrive by
requested, computed by the same estimate asestimated_deliveryfrom the same facility: on a quote, for its own method; on a shipping estimate, for the parcel method listed that allows the latest day. Any time that day counts, and it may be a weekend or a holiday (an order placed then starts counting on the next operating day). Null exactly when no order placed today or later is estimated to arrive byrequested, so it is null whenstatusismisses. It reads the estimate alone, never a carrier advisory. An estimate, like the dates it reads.deliver_by_check.statusstringrequiredThe verdict.
misses: the estimate ends afterrequested.at_risk: the estimate ends by then, but a carrier advisory (advisories) touches the destination while the parcel would be on its way.meets: the estimate ends by then and no advisory touches it.One of
meetsat_riskmissesdeliver_by_check.advisoriesobject[]requiredThe carrier advisories that touch the destination between today and the estimate's latest day; empty when none does.
The same fields as
delivery_risk.advisoriesshippingobjectrequiredThe shipping the figure in
breakdownis for: its method, and the surcharges that figure already holds, itemised. Nothing here is added to the total.shipping.fulfillment_policyobjectOptional exact-five approved fulfillment disclosure. Applies only to the four listed double-sided flags and one Santa hat, each a separate quantity-1 US standard order. Actual manufacturing expense and contribution remain unknown; the shipping benchmark includes packing. Supplies no live mapping by itself.
The same fields as
fulfillment_policyshipping.methodstringrequiredThe shipping method the figure was priced for.
shipping.surchargesobject[]requiredCompatibility itemisation, never added to the total. Current quotes and new orders contain no seasonal peak-postage surcharge: PrintedSimply absorbs the carrier expense, so this array is empty. Historical stored orders may retain their original itemisation; a fee-bearing old quote must be refreshed before a new charge.
shipping.surcharges[].codestringrequiredpeak_season: the carrier's temporary holiday price change.Always
peak_seasonshipping.surcharges[].reasonstringrequiredWhat it is, in words.
shipping.surcharges[].amount_centsintegerrequiredHow much of the shipping figure this is, in cents, folded the way that figure is (all-in where the figure is all-in).
at least 1
shipping.surcharges[].windowobjectrequiredThe days it applies on, both inclusive.
shipping.surcharges[].window.fromstring (date)requiredYYYY-MM-DD, UTC.
shipping.surcharges[].window.throughstring (date)requiredYYYY-MM-DD, UTC.
shipping.surcharges[].sourcestring (uri)requiredWhere the amount and the dates come from: the carrier's own announcement.
shipping.surcharges[].included_instringrequiredAlways
shipping: the figure this is part of. Never an additional charge.Always
shippingpricing_degradedstringSandbox-only. The $0 breakdown beside it is an ABSENCE of a price, not a price — the routing failed and a test key was given a shaped answer anyway.
One of
provider_unavailablepricing_unavailablevariable_dataobject[]One entry per line carrying
variable_data: the variant proofs (first, last and longest unit) and each slot's headroom, before the order is placed. Absent when no line carries it. The breakdown beside it is the same with or without it.variable_data[].item_indexintegerrequiredThe line's position in the request's
items.at least 0
variable_data[].skustringrequiredvariable_data[].unitsintegerrequiredUnits on the line, each printed with its own values.
at least 1
variable_data[].slotsobject[]requiredEach declared slot with its
max_lengthand thelongestvalue any unit gives it: the headroom.variable_data[].slots[].namestringrequiredvariable_data[].slots[].max_lengthintegerrequiredvariable_data[].slots[].longestintegerrequiredCharacters in this slot's longest value on the line.
variable_data[].qrobjectPresent when the line has a QR template: the payload ceiling and the longest payload on the line.
variable_data[].qr.max_lengthintegerrequiredvariable_data[].qr.longestintegerrequiredvariable_data[].proofsobject[]requiredThe variant proofs: the first unit, the last, and the longest (the most printed characters, earliest on a tie), with the exact values and QR payload each will print. A unit that is more than one of the three is listed once, with each variant it stands for. Described rather than drawn: a plain line's artwork carries no slot positions to draw a value into.
variable_data[].proofs[].unitintegerrequiredThe unit's position on the line, 1-based, in print order.
at least 1
variable_data[].proofs[].variantsstring[]requiredvariable_data[].proofs[].valuesobjectrequiredSlot name to the text this unit prints.
variable_data[].proofs[].qr_payloadstringWhat this unit's QR code encodes. Present when the line has a QR template.
variable_data[].live_productionbooleanrequiredWhether a LIVE order carrying this line would be accepted now. False while variable-data production is switched off: sandbox orders run end to end, and a live order or quote is refused with 422 variable_data_unavailable.
meta
pageintegertotalintegerlimitintegerThe page size actually applied, which is not necessarily the one asked for — the operation's own ceiling wins.
pagesintegertotaloverlimit, rounded up.next_cursorstring | nullOn a cursor-paged operation: send this back as
cursorfor the next page. Null on the last page. Absent on operations that do not page by cursor.has_morebooleanOn a list operation whose
metaisListMeta: whether another page follows this one.
{
"status": "success",
"data": {
"quote_id": "qt_EMyYo3DyOSrd7siDgh2niPvP",
"quote_token": "eyJxdW90ZV9pZCI6InF0X0VNeVlvM0R5T1NyZDdzaURnaDJuaVB2UCJ9.3b71988dd79915637f2e65aeddd8893705bb2140ef57a64dcf71aa67148bef1e",
"expires_at": "2026-10-11T08:09:14.578Z",
"lines": [
{
"sku": "sticker-die-cut-3x3",
"quantity": 100,
"unit_cents": 70,
"line_total_cents": 7000
}
],
"breakdown": {
"items_cents": 7000,
"shipping_cents": 650,
"processing_fee_cents": 0,
"platform_fee_cents": 0,
"card_fee_cents": 0,
"total_cents": 7650
},
"margin": {
"suggested_retail_cents": 15666,
"margin_pct": 50
},
"estimated_delivery": {
"earliest": "2026-10-20",
"latest": "2026-10-26",
"ships_by": "2026-10-14",
"is_estimate": true
},
"shipping": {
"method": "standard",
"surcharges": []
}
},
"request_id": "4e3753e1-48dd-4313-bfd8-48e4af4e116a"
}Errors
Invalid request body or parameters. When the request failed the operation's schema,
detailscarriesformErrors(problems with the request as a whole),fieldErrors(messages keyed by top-level field) anderrors: one{ pointer, code, message }per refused value,pointeran RFC 6901 JSON pointer into the body (or into the object of query parameters, for a query parameter) —/items/0/sku, with~written~0and/written~1, and""for the request as a whole. POST /v1/orders/bulk answerserrorsas one{ index, details }per refused order, eachdetailswith its own pointers from the request body (/orders/3/items/0/sku); POST /v1/orders/multi-ship keepserrorsas the{ formErrors, fieldErrors }it always published; POST /v1/mockups/generate and POST /v1/products/recommend keep their fields at the top ofdetails, witherrorsbeside them. A refusal of a rule the schema cannot state (an asset that does not exist, an expiry too soon) names it inmessageand carries noerrors.Missing, invalid or expired API key:
missing_api_key(none sent),invalid_api_key(unknown or revoked),key_expired(past the expiry set when it was created; create a new key).payload_too_large — the request body exceeded this route's byte cap (256 KB unless the route raises it with a written reason). It is refused before it is parsed, so nothing was created. Send fewer rows, or use the endpoint's own asynchronous door where it has one.
422variable_data_invalidvariable_data_unavailabledestination_unavailablebelow_cost_flooraddress_invalidsku_not_orderable_yetsku_not_availableprovider_not_availablecatalog_onlyshipment_capacity_exceededvariable_data_invalid —
items[].variable_datafailed the preflight, before anything was priced or charged.details.problems[]names each problem (up to 50;details.problem_countis the total) byitem_index,unitandslot, with acode. variable_data_unavailable — a LIVE order or quote carries variable data while production is switched off; nothing was charged, and a test key (ps_test_*) accepts the same request end to end. destination_unavailable —address_to.country(elsedestination_country) is outside the US: our own lanes ship to US addresses only.details.reasonisdestination_not_usanddetails.countrynames the destination. below_cost_floor — the order would be made at our own plant and would cost more to make and ship than it is charged, at worst-case postage, and its loss is not one our cost floor accepts: only today's named losses are, no larger than they are today — the flat standard parcel under its postage, for a mailer up to a pound or a poster tube up to two, and list-priced goods short only of the per-order handling cost (or a partner SKU would be charged below what the partner bills for it).details.skusnames the items anddetails.contribution_centshow far below zero the order falls. Nothing was charged; add units, combine it with another order, or choose another shipping method on offer. sku_not_orderable_yet — live keys only: a line names a modeled product our catalog lists that our plant has not confirmed it makes yet.details.skusnames them as sent, anddetails.plant_skusthe spelling our plant adopts for one sent another way. Refused before anything is asked of the plant or charged; a test key orders a modeled listing at its listed price. catalog_only — a flag or Santa hat is a catalog reference whose artwork, production cost and shipping weight are unconfirmed. Automatic quotes and orders are refused in both test and live mode before pricing, routing or charging, including requests carrying an earlier quote;details.skus,details.readinessanddetails.reasonexplain the refusal. shipment_capacity_exceeded — a basket of sized own-plant products exceeds the modeled 70 lb single-parcel limit, including stickers, magnets, posters, decals, packs and rolls. Both test and live requests are refused before charging;details.skus,details.weight_ozanddetails.max_weight_ozstate the affected SKUs, whole-basket estimated weight and limit. Reduce the quantity or split the order. address_invalid — a live quote'saddress_tocannot be right as written:details.findings[]names eacherror(zip_format,state_unknown,zip_state_mismatch, ordelivery_point_not_found) with its field anddetails.address_checkis the whole check, as POST /addresses/validate answers it. A test key is never refused for it; its 200 carriesaddress_check. sku_not_available — a line names a product our own plant does not make: only PrintedSimply's own products (stickers, decals, labels, magnets and posters, as GET /products lists them) are sold here.details.skusnames each one. Asked before anything is priced, so nothing was priced or charged. provider_not_available —providernames a lane other than our own plant. Orders here are made by PrintedSimply: omitprovider, or sendprintedsimply.details.providerechoes what was sent anddetails.availablelists what is accepted. Nothing was priced or charged.Rate limit exceeded. Two windows can produce this status and
error.codesays which:rate_limit_exceededfor the per-minute sliding window,daily_quota_exceededfor the plan's daily quota. Both carryRetry-After; the daily one also carriesX-Daily-LimitandX-Daily-Remaining. The per-minute limits are plan-based — see Rate Limits above, or call GET /v1/me for this credential's own figures.provider_unavailable — the facility that produces these items is not accepting orders right now, so there is no price to quote. Nothing was charged; a quote never charges. With
details.reason: plant_unreachableandRetry-After, our plant gave no answer about a SKU (the request did not reach it, timed out, or came back 5xx) — about the plant, not the SKU; the catalog says the same on the row (not_orderable_code: plant_unreachable). A test key gets a $0 quote markedpricing_degraded: pricing_unavailableinstead. Only for a SKU the catalog has, though: one the price book does not price, our plant's products do not name and the synced catalog does not hold is refused 400 unknown_sku withdetails.skus, as it is on POST /order, POST /orders/quote and POST /pricing/estimate alike. tax_unavailable — sales tax collection is on, the quote has anaddress_to, and Stripe Tax could not calculate its tax, so nothing was quoted; retry shortly, or quote withoutaddress_toto have the tax calculated when the order is placed.