Submit up to 50 orders in a single API call. Each order is processed independently — partial success is possible. Returns per-order results. An order may carry a quote_token from POST /orders/quote and is then charged that quote's locked total instead of being re-priced; the honored quote_id comes back on the result. A token that is expired, issued to another key, or does not match the order's line items and shipping method fails that one order, not the batch. With a test key the whole batch is validated exactly as a live batch and every order is persisted as a real sandbox order (no charge, not sent to production), so the returned ids work with GET /order/{id} and run the time-compressed lifecycle. An order whose reference another order already carries — placed earlier, or another entry of this batch — fails that one order, uncharged, with an error starting duplicate_reference: that names the order holding it, as POST /order refuses it with 409; allow_duplicate_reference: true on the entry places it anyway. An entry whose reference could not be checked fails alone, uncharged, with an error starting reference_check_unavailable:.
- Credential
- An API key in
x-api-key - Scopes
orders:write
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
ordersobject[]required1 to 50 entries
orders[].address_toobjectrequiredorders[].address_to.namestringrequirednon-empty
orders[].address_to.companystringorders[].address_to.address1stringrequirednon-empty
orders[].address_to.address2stringorders[].address_to.citystringrequirednon-empty
orders[].address_to.statestringrequirednon-empty
orders[].address_to.zipstringrequirednon-empty
orders[].address_to.countrystringrequiredISO 3166-1 alpha-2 country code
exactly 2 characters
orders[].address_to.phonestringorders[].address_to.emailstring (email)orders[].address_fromobjectThe same fields as
orders[].address_toorders[].shippingobjectorders[].shipping.carrierstringorders[].shipping.methodstringThe shipping tiers this platform prices. Per parcel, not per item:
standardby the parcel's weight class (POST /v1/shipping/estimate reports it), and for items made at the partner facility (apparel, mugs) at the larger of that and the partner's own estimate for the parcel;priorityandexpressat their price and never below standard for the same parcel. An unrecognised value is refused with 400 validation_error rather than priced asstandard.standardis always offered;priorityandexpressare offered when priced (each at or above the USPS cost of the service), and until then a request for either is refused 400shipping_method_unavailable, withdetails.offerednaming the methods that are, and nothing is charged.letteris untracked letter mail for stickers and small vinyl decals from our own plant, offered only where the platform prices it, for an order whose every item qualifies, within a unit and an order-value ceiling: otherwise a request for it is refused 400letter_mail_unavailablewithdetails.reason, and nothing is charged. On the API it is never chosen for you — no default, estimate or storefront mapping picks it — and it is placed through POST /v1/order only (bulk and bundle orders ship by parcel). A hosted store is the one exception, and only where its merchant turned on small orders by letter in the dashboard's store editor (off by default, launched stores included): a qualifying basket bought on its store page or a buy link ships as a letter, and the shopper is told before paying. A letter has no carrier tracking: its order events and tracking page state the expected window, and POST /v1/order/{id}/missing-letter reships it once if it does not arrive.One of
standardpriorityexpressletterorders[].itemsobject[]required1 to 100 entries
orders[].items[].skustringrequirednon-empty
orders[].items[].quantityintegerrequired1 to 10000
orders[].items[].artwork_urlstring (uri)The artwork for this line, as a public http(s) URL. Printed when set; otherwise the line's
print_fileis. Send one of the two: a line with neither has nothing to print.orders[].items[].print_filestringThe artwork for this line: a public http(s) URL, or the id of a file you uploaded with POST /assets (
ast_…), resolved to that file's URL. Read whenartwork_urlis not set.orders[].items[].variable_dataobjectA different name, number or QR payload on every unit of this line. Accepted only on flat goods made at our own plant (stickers, decals, magnets, posters); an order carrying it is produced there. Declare the named slots of the line's design, then give every unit a value for every slot: from
units(one entry per unit, so its length must equalquantity), from asequencethat fills one slot, or both. An optionalqr.templatebuilds each unit's QR payload from its values. Preflighted before anything is priced or charged: a value longer than its slot, aunitsarray of the wrong length, an undeclared slot, a missing value or another plant's SKU is refused with 422variable_data_invalid, naming each problem by item, unit and slot. Never changes the price. Live orders and quotes carrying it are refused with 422variable_data_unavailableuntil production is switched on; sandbox (ps_test_*) orders accept it end to end.orders[].items[].variable_data.slotsobject[]requiredThe named text slots of the line's design, each with the most characters it holds.
1 to 10 entries
orders[].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
orders[].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.orders[].items[].variable_data.qrobjectEach unit's QR payload, built from its values.
orders[].items[].artworkobjectOptional: how this line's artwork was made, and who approved it and when. A declaration you state, stored on the line as sent (approved_by sanitized, approved_at in UTC), echoed on the order's reads (
line_items[].artwork) and on itsorder.createdwebhook. It never changes the price, the routing or the artwork check, and a line without it is placed exactly as before. Strict: an unknown key is a 400 validation_error.orders[].items[].artwork.provenancestringHow the artwork was made:
human(made by a person),ai_assisted(a person made it with a model's help) orai_generated(a model made it).One of
humanai_assistedai_generatedorders[].items[].artwork.approved_bystringWho approved the artwork for print: a name or an email address, at most 200 characters. Control and invisible formatting characters are removed and whitespace collapsed before it is stored. Send it with approved_at.
non-empty · at most 200 characters
orders[].items[].artwork.approved_atstring (date-time)When the artwork was approved: an ISO 8601 date-time with a time zone, not in the future. Stored and echoed in UTC. Send it with approved_by.
orders[].providerstringWhere the order is made: always our own plant here. Omit it, or send
printedsimply; any other value is refused with 422provider_not_availablebefore anything is priced or charged.Always
printedsimplyDefault printedsimply
orders[].referencestringYour own identifier for this order — a marketplace order number, a cart id, whatever you key on. At most 200 characters, and no NUL byte. Stored verbatim, echoed on the create response, on GET /orders and GET /order/{id}, and on the order webhooks, and filterable with GET /orders?reference=. One reference is one order: while an order of your account placed in the last 30 days with a key of the same mode (a test key against test orders) and not cancelled carries it, another order with the same reference is refused 409
duplicate_referencebefore anything is charged, whatever its Idempotency-Key, anddetails.order_idnames the order that has it. Use Idempotency-Key for exactly-once retries of one request; this catches the duplicate sent under a new key. When whether the reference is free cannot be checked, the order is not placed: 503reference_check_unavailable, nothing charged.at most 200 characters
orders[].allow_duplicate_referencebooleanPlace the order even when another order already carries its
reference(seereference). For a reference that is not unique on your side, such as order numbers from two stores that each count from 1. Omit it, or send false, to have a duplicate refused with 409duplicate_reference.Default false
orders[].vatstringTax-registration number (VAT / IOSS / GST) to declare for this shipment. Stored, echoed on the create response and on GET /orders and GET /order/{id}, and forwarded to the fulfilling facility for the customs declaration. Supply it per order — an international run can ship to several destinations under different registrations. Omit it on domestic shipments.
orders[].quote_tokenstringA signed quote from POST /orders/quote. When present, the order is charged exactly that quote's locked total instead of being re-priced and the quoted facility routing is honored. Refused (400) when expired, issued to a different key, or when the order's items or shipping method differ from what was quoted.
orders[].payment_intentstringLive keys only. When a live order's card needs 3D Secure, the order is answered 402
authentication_requiredwithdetails.payment_intentanddetails.client_secret: the amount opened on the card as an authorization only, nothing charged. Have the cardholder approve it in their browser with Stripe.js, loaded withdetails.publishable_key(the intent is on our Stripe account), callinghandleNextAction({ clientSecret }), then send the same order again with this field set todetails.payment_intentand a new Idempotency-Key. The order captures that authorization instead of charging the card again. It is refused with 402payment_failedunless it was opened for this account, authorizes exactly this order's total and has not been captured —details.codesays which check failed — and one the cardholder has not finished approving answersauthentication_requiredagain. An approval no order captures is released by the card network on its own. Ignored on a test key, which charges nothing.matches ^pi_[A-Za-z0-9]+$
orders[].approvalstringrequiredholds the order for a person's approval before anything is charged: the order is checked and priced exactly as without it (every refusal it would get, it gets now), then recordedpending_approvalwith nothing charged, nothing counted against the key's spend caps and nothing sent to production, and answered 202OrderAwaitingApprovalwithapproval_url, a signed page the person opens with no account. It shows each line's artwork, product, size, quantity and price, the total, the town it ships to and the line's artwork declaration. Approving it places the order through this same operation, as the key it was held with, at the total the person was shown: the checks run again, the card is charged and the order goes to production (order.approval_approved, thenorder.createdandorder.paid). If the price has moved, the card is declined or a spend cap refuses it, nothing is charged,approval.statusreadsfailedwith the reason, and the person may approve it again. Declined, or not decided within 72 hours (approval_expires_at), the order is cancelled with nothing charged (order.approval_declined,order.approval_expired); DELETE /order/{id}/cancel cancels it too, with nothing to refund. A held order takes no address, shipping or line change. Aquote_tokenis honoured at approval only while it is valid (24 hours): approved later, the order places nothing (invalid_quote). Taken on an API key's order only (a platform OAuth token's is 400 validation_error,details.field: approval) and not withpayment_intent. On POST /orders/bulk an entry carrying it fails validation.Always
required
Request
POST /api/v1/orders/bulk
curl -X POST "https://portal.printedsimply.com/api/v1/orders/bulk" \
-H "x-api-key: ps_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-bulk-orders-$(date +%s)" \
-d '{
"orders": [
{
"items": [
{
"sku": "sticker-die-cut-3x3",
"quantity": 100,
"artwork_url": "https://portal.printedsimply.com/samples/sticker-3x3.png"
}
],
"address_to": {
"name": "Jane Doe",
"address1": "123 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701",
"country": "US"
},
"shipping": {
"method": "standard"
}
},
{
"items": [
{
"sku": "sticker-die-cut-3x3",
"quantity": 100,
"artwork_url": "https://portal.printedsimply.com/samples/sticker-3x3.png"
}
],
"address_to": {
"name": "Sam Lee",
"address1": "420 Pine St",
"city": "Seattle",
"state": "WA",
"zip": "98101",
"country": "US"
},
"shipping": {
"method": "standard"
}
}
]
}'Response 200
Bulk order results — one row per entry, partial success inside a 200. An entry our plant gave no answer about (the request did not reach it, timed out, or came back 5xx — POST /order's 503 provider_unavailable with details.reason: plant_unreachable) fails alone, uncharged, with an error starting plant_unreachable:, and the 200 carries Retry-After; a test key's entry is recorded at $0 instead, as on POST /order. Place the failed entries again in a new request: a replay of this one under the same Idempotency-Key returns these same rows.
data
totalintegerrequiredsucceededintegerrequiredfailedintegerrequiredresultsobject[]requiredresults[].indexintegerrequiredPosition of this entry in the request's orders array
results[].statusstringrequiredOne of
successerrorresults[].order_idstringThe public order id, on success
results[].portal_order_idstringresults[].providerstringThe facility that took the order
results[].chargedstringWhat was charged for this entry, e.g. '$23.79'
results[].tracking_urlstring (uri)results[].test_modebooleanresults[].quote_idstringSet when this entry was charged a signed quote's locked total
results[].referencestringYour own identifier for this entry, echoed back
results[].artwork_checkobjectOn success: the verdict on each of this entry's lines made at our own plant, numbered by the entry's own item_index, as POST /order's artwork_check. Absent when no line has one and, on a test key, no own-plant line lacks one.
results[].artwork_check.linesobject[]requiredOne entry per line made at our own plant (stickers, decals, magnets, posters) that carries artwork, in item order. A line another lane makes is graded there, not here.
results[].artwork_check.lines[].item_indexintegerrequiredThe line's position in the request's
items, 0-based.at least 0
results[].artwork_check.lines[].skustringrequiredresults[].artwork_check.lines[].statusstringrequiredpassed: read, nothing to say. warned: read, at least one warning. refused: at least one error (a live order is refused). unverified: the file could not be read just now (see the
unverifiedfinding); the order is not refused for it.One of
passedwarnedrefusedunverifiedresults[].artwork_check.lines[].width_pxintegerThe file's width in pixels. Absent when it was not read, or is vector.
at least 1
results[].artwork_check.lines[].height_pxintegerThe file's height in pixels.
at least 1
results[].artwork_check.lines[].print_size_innumber[]The print size resolution was measured against, width and height in inches: the SKU's
WxH, else the line'ssize. Absent when neither names one.2 to 2 entries
results[].artwork_check.lines[].effective_dpiintegerPixels per inch with the artwork printed as large as the print size allows (turned a quarter if that fits it larger).
at least 1
results[].artwork_check.lines[].min_dpiintegerThe product's recommended minimum DPI. Below 75% of it is an error.
at least 1
results[].artwork_check.lines[].findingsobject[]requiredresults[].artwork_check.missing_artworkobject[]Test keys (ps_test_*) only, on POST /order, POST /orders/bulk and POST /{id}/item: the lines made at our own plant that carry no artwork at all, which a live request is refused for with 422
artwork_required. Absent when there are none.results[].artwork_check.missing_artwork[].order_indexintegerPOST /orders/bulk's 422 only: the order's position in
orders.at least 0
results[].artwork_check.missing_artwork[].item_indexintegerrequiredThe line's position in the order's
items, 0-based.at least 0
results[].artwork_check.missing_artwork[].skustringrequiredresults[].artwork_check.would_refusebooleanTest keys (ps_test_*) only: whether a live order with these lines would have been refused, with 422
artwork_not_printableor (for a line inmissing_artwork)artwork_required. A test key is never refused for artwork, so placeholder artwork still places a sandbox order. Absent on a live order, which either was refused or was not.results[].address_checkobject | anyWhat POST /addresses/validate finds in this entry's
address_to, warnings included: POST /order'saddress_check. A live entry with anerrorfinding is refused (error_code: address_invalid) before anything is charged; on a test keyvalid: falsehere says a live entry would have been. Null only where the check did not run.results[].address_check.validbooleanrequiredFalse when any finding is an
error.results[].address_check.checkedbooleanrequiredFalse for an address outside the US, which is passed through as written.
results[].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.
results[].address_check.normalized.namestringresults[].address_check.normalized.companystringresults[].address_check.normalized.address1stringrequiredresults[].address_check.normalized.address2stringresults[].address_check.normalized.citystringrequiredresults[].address_check.normalized.statestringrequiredresults[].address_check.normalized.zipstringrequiredresults[].address_check.normalized.countrystringrequiredresults[].address_check.normalized.phonestringresults[].address_check.normalized.emailstringresults[].address_check.findingsobject[]requiredresults[].address_check.findings[].codestringrequiredThe
delivery_point_*codes come from delivery-point verification, which only a live POST /order, POST /orders/bulk entry or PUT /order/{id}/address runs, on a US address the other checks pass, and only where the deployment has USPS verification configured:delivery_point_not_found(error) — USPS answered that no delivery point exists at the street address;delivery_point_incomplete(warning) — the building exists but its unit is missing or not confirmed, or more than one address matched;delivery_point_confirmed(info). When USPS cannot be reached the order proceeds without them.One of
zip_formatstate_unknownzip_state_mismatchzip_state_borderzip_unassignedpo_boxmilitary_statemilitaryaddress2_splitnot_checkeddelivery_point_not_founddelivery_point_incompletedelivery_point_confirmedresults[].address_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
errorwarninginforesults[].address_check.findings[].fieldstringrequiredOne of
address1address2citystatezipcountryresults[].address_check.findings[].messagestringrequiredresults[].estimated_deliveryobject | nullOn success: when this entry's order should arrive, as POST /order's
estimated_deliverydates it. Null on a refused entry and on a lane that is not one of our own facilities.results[].estimated_delivery.earlieststring (date)requiredYYYY-MM-DD, UTC: the soonest the order should arrive.
results[].estimated_delivery.lateststring (date)requiredYYYY-MM-DD, UTC: the latest it should, before anything goes wrong.
results[].estimated_delivery.ships_bystring (date)requiredYYYY-MM-DD, UTC: the day it should leave the facility, when its production estimate ends, counted the same way.
results[].estimated_delivery.is_estimatebooleanrequiredAlways true: say it as an estimate wherever it is shown.
Always
trueresults[].errorstringWhy this entry was refused, on error. Every refusal the route words itself starts with its code (
destination_unavailable:,duplicate_reference:,invalid_quote:,spend_cap_exceeded:,payment_failed:, …), whicherror_codecarries as a field of its own;plant_unreachable:means our plant gave no answer about a SKU — nothing was charged, and the entry may be placed again after the response'sRetry-After.production_reconciliation_required:means production may exist and support must reconcile it; do not refund or resubmit this entry under another key.fulfillment_error:andorder_error:entries were charged: the sentence says whether the payment was refunded or the order stands.results[].error_codestring | nullThe code
errorstarts with, to branch on without splitting the string: the code POST /order answers the same refusal with (plant_unreachableis POST /order'sdetails.reasonon its 503provider_unavailable). Null on success, and on an error the route did not word (an unexpected failure, whoseerrorstarts with no code).One of
address_invalidauthentication_requireddestination_unavailableduplicate_referencefraud_reviewfulfillment_errorinvalid_quoteorder_errorpayment_failedplant_unreachablepricing_errorproduction_reconciliation_requiredprovider_cannot_produceprovider_unavailablequote_mismatchreference_check_unavailablespend_cap_exceededunknown_sku
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": {
"total": 2,
"succeeded": 2,
"failed": 0,
"results": [
{
"index": 0,
"status": "success",
"order_id": "test_ord_qbpi4ALIbckcSICFfIPmmOKl",
"portal_order_id": "ord_qbpi4ALIbckcSICFfIPmmOKl",
"provider": "merchfarm",
"charged": "$0.00",
"tracking_url": "https://portal.printedsimply.com/track/eyJ2IjoxLCJ0IjoidHJhY2tpbmciLCJ0b2siOiJ0cmtfckVOcGFBcXFhY3ZWWUZubVdZcGhYUGdiIiwiZXhwIjoiMjAyNy0wMS0xM1QxNTowMDoxOC4wMDBaIn0.1ff490e6f8eebee240ec6a31f301d2bb46d857291a20c15e906488dc38e80ed8",
"test_mode": true,
"artwork_check": {
"lines": [
{
"item_index": 0,
"sku": "sticker-die-cut-3x3",
"status": "passed",
"width_px": 1200,
"height_px": 1200,
"findings": [],
"print_size_in": [
3,
3
],
"effective_dpi": 400,
"min_dpi": 300
}
],
"would_refuse": false
},
"estimated_delivery": {
"earliest": "2026-10-23",
"latest": "2026-10-29",
"ships_by": "2026-10-19",
"is_estimate": true
},
"address_check": {
"valid": true,
"checked": true,
"normalized": {
"name": "Jane Doe",
"address1": "123 MAIN ST",
"city": "AUSTIN",
"state": "TX",
"zip": "78701",
"country": "US"
},
"findings": []
},
"error_code": null
},
{
"index": 1,
"status": "success",
"order_id": "test_ord_JyZLq7sUxTZxc7qjzI76ZQYU",
"portal_order_id": "ord_JyZLq7sUxTZxc7qjzI76ZQYU",
"provider": "merchfarm",
"charged": "$0.00",
"tracking_url": "https://portal.printedsimply.com/track/eyJ2IjoxLCJ0IjoidHJhY2tpbmciLCJ0b2siOiJ0cmtfaklVQXZFTTl6UXZMank4d3lWUmZwOUpvIiwiZXhwIjoiMjAyNy0wMS0xM1QxNTowMDoxOC4wMDBaIn0.c342585c817ecadf771a6b3c3a8c0b0af13404c28dd1bca7129bd38c102ec43f",
"test_mode": true,
"artwork_check": {
"lines": [
{
"item_index": 0,
"sku": "sticker-die-cut-3x3",
"status": "passed",
"width_px": 1200,
"height_px": 1200,
"findings": [],
"print_size_in": [
3,
3
],
"effective_dpi": 400,
"min_dpi": 300
}
],
"would_refuse": false
},
"estimated_delivery": {
"earliest": "2026-10-23",
"latest": "2026-10-29",
"ships_by": "2026-10-19",
"is_estimate": true
},
"address_check": {
"valid": true,
"checked": true,
"normalized": {
"name": "Sam Lee",
"address1": "420 PINE ST",
"city": "SEATTLE",
"state": "WA",
"zip": "98101",
"country": "US"
},
"findings": []
},
"error_code": null
}
]
},
"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).402Payment failed or no payment method configured
403sandbox_onlysandbox_only — the account is restricted to sandbox mode and the request used a live key. The whole batch is refused before any row is placed.
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_unavailableartwork_not_printableartwork_requiredsku_not_availableprovider_not_availablevariable_data_invalid — an entry's
items[].variable_datafailed the preflight; the whole batch is refused before any row is placed or charged, and each ofdetails.problems[]carries the entry'sorder_indexbeside its item, unit and slot. variable_data_unavailable — a live batch carries variable data while production is switched off; refused whole, nothing charged. artwork_not_printable — a live batch has a line made at our own plant whose artwork cannot be printed as it is (unreachable, not_an_image, low_resolution); the whole batch is refused before any row is placed or charged, each ofdetails.problems[]names the line byorder_indexanditem_index, anddetails.artwork_checks[]carries every checked order's verdicts. artwork_required — a live batch has a line made at our own plant with no artwork at all (no artwork_url, no print_file); the whole batch is refused before any file is read or any row is placed or charged, anddetails.items[]names each line byorder_index,item_indexandsku. A test key is never refused for artwork; each row'sartwork_check.would_refusesays what a live batch would do, andartwork_check.missing_artworklists a row's lines with none. See POST /order's 422. sku_not_available or provider_not_available — an entry names a product our own plant does not make, or a lane other than our plant: the whole batch is refused before anything is priced or charged, anddetails.errors[]names each refused entry byindexwith itscodeand itsskus, or itsproviderandavailable.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.idempotency_unavailable — the request carried an
Idempotency-Keyand the ledger that makes it mean at-most-once could not be reached, so nothing ran and nothing was charged; retry the same request with the same key afterRetry-After.