One dated version with purchase identity on every line, retry-safe order commands, ordered listing snapshots and recoverable order state.
The launch contract. 2026-10 is the only supported version and every platform is pinned to it; X-Snapcaster-Api-Version naming any other version, the pre-launch 2026-07 and 2026-09 included, answers 400 UNSUPPORTED_API_VERSION. Every order line, every line of an order.cancelled or order.refunded event and every line of a pending cancellation request carries line_ref, the purchase identity; At launch, cancellation and refund history lines carried sku_id and quantity only; a later additive change below gives them line_ref. cancelOrder and refundOrder name lines by skuId or lineRef; lineRef is needed only when one SKU is on more than one line the command can still act on, which ORDER_LINE_AMBIGUOUS reports. Order commands require a UUID Idempotency-Key: a retry with the same key and body answers the original status and JSON value, without keeping object key order; changed contents, or a key first used from the seller dashboard, answer 409 IDEMPOTENCY_CONFLICT. Every listing write carries asOf and is applied as an ordered snapshot. GET /orders and GET /orders/{orderId} answer complete current order state, and the final page's syncToken recovers every later change, transactions that commit late included. order.updated carries that same order plus its reason. The 2xx on the event that announced a sale acknowledges its stock, and a fulfillment report acknowledges nothing: an orders subscription deducts stock on order.created and order.updated before returning 2xx and needs a registered webhook endpoint, and a both subscription deducts only on inventory.adjusted. A positive inventory.adjusted that returns sold units is sent only after the platform acknowledged the sale it compensates, and never if that sale is parked dead unacknowledged. An inventory subscription receives 403 SUBSCRIPTION_EXCLUDES_ORDERS from the order routes. Webhook retries repeat the same payload bytes, except that after Snapcaster corrects a guest buyer's mistyped email every later order.created and order.updated delivery carries the corrected customer.email, and nothing else in them changes. A path or method no operation serves answers 404 with the JSON body {"status":"error","message":"Cannot GET <path>","code":"NOT_FOUND"}; a body that does not parse, a path segment that is not valid percent-encoding, and a missing required body answer the operation's 400 INVALID_REQUEST after the API key is checked; a body larger than the operation accepts answers 413 PAYLOAD_TOO_LARGE. GET /me lists every incomplete readiness prerequisite, and its OPERATOR_REVIEW_APPROVED entry carries a review status.
- Required action
- Build against
2026-10. Key order lines online_ref, send a UUIDIdempotency-Keyon every order command andasOfon every listing write, and save the finalsyncTokenof each order poll. With anorderssubscription, register a webhook endpoint and return2xxonorder.createdandorder.updatedonly after deducting their stock. - Migration or testing
- No migration for a new integration. Walk the Sandbox quickstart, then test a lost-response retry of an order command and a recovery pass with
syncTokenin the sandbox. Webhook deliveries still queued when2026-10launched are not sent if they cannot be stated in its shape: every pre-launchorder.updated, anorder.createdor saleinventory.adjustedfor an order whose line a substitution rewrote with no record of that line as bought, and a positiveinventory.adjustedreturning units no retained sale took or whose sale was not delivered. Recover those orders withGET /orders/{orderId}or your savedsyncToken.
Additive changes
upsertListingsandbulkWriteListings:productIdis optional, and Snapcaster takes each listing's product from the canonical catalog entry for itsskuId. An item whoseproductIdnames a different product than the catalog holds for itsskuIdis rejected with the new per-item codePRODUCT_MISMATCH, and Snapcaster stores nothing for it. An item whoseskuIdis not yet in the catalog still needsproductId, and when the catalog gains that SKU the listing takes the catalog's product.getProduct: eachproduct.details[]entry gainsruns, its value as text runs and line breaks withemphasismarking the text the catalog emphasized, andvalueis now plain text with the catalog markup removed and each line break as a newline.- Every webhook event (
orderCreatedWebhook,orderUpdatedWebhook,orderCancelledWebhook,orderRefundedWebhook,inventoryAdjustedWebhook): Snapcaster now retries a failing delivery for at least 24 hours (37 attempts, the wait doubling from 30 seconds up to one hour, with up to 20% random variation) before holding it for replay, up from 8 attempts over about one hour, so ask for a replay only after an outage longer than 24 hours. A replayed event keeps its originalevent_idand carries the same payload bytes as its earlier attempts, except that a corrected guest email reaches it incustomer.email. - Every webhook event (
orderCreatedWebhook,orderUpdatedWebhook,orderCancelledWebhook,orderRefundedWebhook,inventoryAdjustedWebhook): an event raised before your platform registers its webhook endpoint now waits unsent, spending none of its retry attempts, and is delivered once the endpoint is registered, where before it spent its attempts and was held for replay. getOrder,listOrdersandorderUpdatedWebhook: eachcancellations[].linesandrefunds[].linesentry gainsline_ref, the purchase the cancellation or refund terminated, the sameline_refitsorder.cancelledororder.refundedline carries. It isnullonly on a cancellation recorded before Snapcaster kept an order event log, when itssku_idnames more than one line on the order or a substitution has rewritten a line on that order; every refund line carries it.getStoreandupdateStore: the store gainssellerId, the seller's numeric id and the same value asdata.seller_idon every webhook event for that seller, so a platform can match events to the store its key belongs to before the first sale.getOrder,listOrdersandorderUpdatedWebhook: each order line gainssubstitution_credit_cents, the credit per unit its accepted substitutions returned (0 when none),substitution, the fulfillment report that last rewrote it and the card it replaced (null on a line that ships as sold), andreported_line_ref, always null today. The order gainssubstitutions[], one entry per accepted fulfillment report that substituted a line, with each removed or substituted line, its per-unit credit and the report'spayment_refund.orderUpdatedWebhookgainsreason: payment_refund_updated, sent each time thepayment_refund.statusof a cancellation, refund or substitution changes after its first event, including a settled refund the bank later returns and a failed one a later retry settles. A refund returned on several payments readsfailedif any of them refused it,succeededonce all settled, andpendingotherwise.createSandboxTestPurchaseandgetSandboxTestPurchase, sandbox only: a platform buys its own seller's browsable listings as Snapcaster's sandbox buyer, paid in Stripe test mode, and gets the usual signedorder.createdorinventory.adjustedonce the capture creates the order, so the Sandbox quickstart's test purchase no longer waits on Snapcaster. The production API does not serve these operations.correctOrderAddressandretryOrderAddressVerification: two new order commands, each with a UUIDIdempotency-Key.POST /orders/{orderId}/correctionsmoves the order'sship_toto a corrected name and address while it iscreatedoraccepted, keepscustomeras the buyer entered it, records your API key as the author and sendsorder.updatedwithreason: address_corrected; it answers409 CORRECTION_NOT_ALLOWEDpastaccepted,422 CORRECTION_CHANGES_DELIVERY_REGIONfor a new province or country and422 CORRECTION_UNCHANGEDfor the currentship_to.POST /orders/{orderId}/address-verification/retryruns the address check again whileaddress_verification.statusispendingorfailedand sendsorder.updatedwithreason: address_verifiedwhen a result lands; it answers429 ADDRESS_VERIFICATION_RETRY_TOO_SOONwithin a minute of a failed check and409 ADDRESS_VERIFICATION_NOT_RETRYABLEonce the check has a verdict. A replay of either command answers the stored response, so a retried address check never runs a second lookup.listCancellationRequestsanddeclineCancellationRequest: a platform reads every cancellation request on an order, pending and resolved, and declines a pending one with areason. Snapcaster emails the buyer the same notice a decline in the seller dashboard sends. A decline needs a UUIDIdempotency-Keyand no step-up, and aninventorysubscription receives403 SUBSCRIPTION_EXCLUDES_ORDERSfrom both. Three webhook events,order.cancellation_request.created,order.cancellation_request.withdrawnandorder.cancellation_request.resolved(withoutcomehonoredordeclined), reachordersandbothsubscriptions. Each carries the request as the list returns it plusseller_id, and its2xxacknowledges no stock.getOrder,listOrdersandorderUpdatedWebhook:dispatchgainsoverdue_at, when Snapcaster marked the order overdue for missing its ship-by deadlinedispatch.deadline_at(the end of the third Toronto business day after the order was placed), andpayout_paused, true while the seller payout for the order is paused. A new webhook event,order.dispatch_overdue(orderDispatchOverdueWebhook), reachesordersandbothsubscriptions once per order when it goes overdue, withorder_id,seller_id,deadline_at,overdue_atandpayout_paused: true; its2xxacknowledges no stock. A missed event is recovered through thesyncTokenpoll.listChangeRequests,acceptChangeRequestanddeclineChangeRequest: a platform reads every change request on an order (a buyer address change, a buyer removal of lines or of the whole order, and a seller fulfillment report), each line naming itsline_ref, and accepts or declines a pending buyer request. An accepted address change movesship_to, restarts address verification and sendsorder.updatedwithreason: address_corrected; an accepted removal cancels the lines before shipping or refunds them after, with the optionalrestock, keeping the buyer's purchase fee as a dashboard accept does. Both answers need a UUIDIdempotency-Keyand no step-up, email the buyer as the dashboard does, and answer409 ORDER_CHANGE_REQUEST_NOT_PENDINGand422 ORDER_CHANGE_REQUEST_NOT_ALLOWEDas the dashboard does; aninventorysubscription receives403 SUBSCRIPTION_EXCLUDES_ORDERS. Two webhook events,order.change_request.createdandorder.change_request.resolved(withoutcomeaccepted,declined,withdrawn,voided,removed_insteadororder_cancelled), reachordersandbothsubscriptions for every request, whoever answers it. Each carries the request as the list returns it plusseller_id, its2xxacknowledges no stock, and every change to a request brings the order back into thesyncTokenpoll.sendWebhookTest:POST /webhooks/testwith a UUIDIdempotency-Keyqueues one signedwebhook.testevent,data{seller_id, platform_id}, to your registered endpoint, signed and retried like any event, and answers202with itsevent_id. Every subscription can call it in every environment, and it changes no order and no stock. A repeated key returns the sameevent_id. It answers409 WEBHOOK_ENDPOINT_NOT_REGISTEREDwhen your platform has no endpoint and429 RATE_LIMITEDwithRetry-Afterpast 5 test events a minute for each platform.listShippingOptions, available now: a platform reads its seller's shipping options with the shipping type taxonomy they are drawn from, and in the sandbox uses an option'sidasshippingOptionIdoncreateSandboxTestPurchase. Add-on offers, which will name an upgrade by an option'sid, are not yet available.- Not yet available: order additions are switched off in production and in the sandbox, so no order can take an add-on and none of the three add-on events is sent yet; this entry describes the contract for when they open, which a later entry will announce.
listOrderAdditionRequests,declineOrderAdditionRequest,listOrderAdditionOffers,sendOrderAdditionOfferandwithdrawOrderAdditionOffer: a platform reads a buyer's requests to add cards to an order and the offers sent on it, declines a pending request, and sends, replaces or withdraws a priced offer, which reserves its units for 24 hours. Each command needs a UUIDIdempotency-Keyand acts as the API key; it refuses an offer with the codes the seller dashboard answers, and aninventorysubscription receives403 SUBSCRIPTION_EXCLUDES_ORDERSfrom every one of these routes. The buyer still approves and pays on Snapcaster. Three webhook events reachordersandbothsubscriptions:order.addition_request.created,order.addition_request.resolved(itsstatusoffered,declinedorwithdrawn), andorder.addition_offer.updatedwith areasonfor each offer change, fromsenttopaid. Each carries the resource as its list returns it plusorder_idandseller_id, and its2xxacknowledges no stock: a paid offer's units are still announced, and acknowledged, byorder.updatedwith reasonitems_added. listSubstituteCandidates,createChangeRequestandwithdrawChangeRequest: a platform files a fulfillment report for lines the seller cannot ship as sold, before the order ships, and withdraws it before the buyer answers. Each report line is named bylineReforskuIdand is either removed (action: remove, optionalquantity) or substituted whole (action: substitute) withsubstituteSkuId, another SKU of the line's catalog product that the seller lists live, andcreditCents, the credit per unit. Snapcaster reads the product from its catalog, never from the listing, and resolves the SKU to the seller's own listing.GET /orders/{orderId}/substitute-candidateslists the SKUs that qualify for a line, each with itsexternal_ref,availableandunit_price; availability is provisional until the buyer answers. Filing and withdrawing need a UUIDIdempotency-Keyand no step-up, and are refused with the seller dashboard's codes (422 ORDER_CHANGE_REQUEST_NOT_ALLOWEDfor another catalog product, a SKU the seller does not list or a substitution smaller than the line's remaining units,400 ORDER_LINE_AMBIGUOUSwith the candidatelineRefsindetails); aninventorysubscription receives403 SUBSCRIPTION_EXCLUDES_ORDERS. The report reaches the platform asorder.change_request.created, and the buyer's answer or the withdrawal asorder.change_request.resolved; an accepted substitution sendsorder.updatedwithreason: line_substituted, keeping the line'sline_ref, quantity andunit_priceand adding the credit, and removed units sendorder.cancelled. Each change request'ssubstitutegainsexternal_ref, your reference on the substitute listing.createSandboxCancellationRequest,createSandboxChangeRequestandanswerSandboxChangeRequest, sandbox only: Snapcaster's sandbox buyer raises a cancellation request, an address change or a removal on an order from the seller's own test purchase, or answers the seller's fulfillment report withaccept,remove_insteadorcancel_order, through the same buyer action a real buyer takes, so a platform can walk every Path B order command from the docs alone. Each takes a UUIDIdempotency-Key, needs anordersorbothsubscription (aninventoryplatform receives403 SUBSCRIPTION_EXCLUDES_ORDERS), sends the same webhooks the buyer's action sends, and answers404 ORDER_NOT_FOUNDfor an order no test purchase of the seller created. The production API does not serve these operations. The credentials page also lists three sandbox catalog SKUs of one product, so a substitution can be filed.ChangeRequest,ChangeRequestEventDataandChangeRequestResolvedData:addressandprevious_addressare now documented as an address ornull, as they were always sent and as their descriptions said: set only on anaddress_change, null on every other kind. The schema, not the data, changed; a platform that rejected the null against the old schema can accept it now.getStore: theOPERATOR_REVIEW_APPROVEDandAUTHENTICATOR_RECOVERY_VERIFIEDreadiness actions (CONTACT_MARKETPLACE_OPERATIONS) now linkmailto:info@snapcaster.gg, the address the guides name for the store review, in place of the buyer and seller support inbox.CONTACT_SUPPORTactions are unchanged.createSandboxCancellationRequest, sandbox only: theMarketplaceErrorcode enum now allowsREQUEST_ALREADY_OPENandCANCELLATION_REQUEST_LIMIT_REACHED, which its409lists, andCANCELLATION_NOT_ALLOWED, which its422lists: the codes those answers always carried. The schema, not the data, changed; a platform that rejected these answers against the old schema can accept them now.getStoreandupdateStore: the store gainssubscription, your platform's subscription (orders,inventoryorboth, ornullfor a seller linked to no platform), so you can read it instead of inferring it from an order operation's403 SUBSCRIPTION_EXCLUDES_ORDERS.getStoreandupdateStore: theVACATION_DISABLEDreadiness action (DISABLE_VACATION) now linksPATCH /api/v1/marketplace/me, which the marketplace API key can send with{"vacation": false}, in place of the account API route/api/v1/marketplace/seller/methat refuses the key with401. Every other readiness action is unchanged, and the seller dashboard's own readiness list still links the account API route.
- API versioning
- Webhook signing
- Get store status
- Upsert listings
- Bulk upsert/delete listings
- Soft-delete listings
- Purge all listings
- Poll orders
- Get one order
- Update fulfillment
- Cancel an order
- Create a refund
- List an order's cancellation requests
- Decline a cancellation request
- List an order's change requests
- Accept a change request
- Decline a change request
- Send a test event to your webhook endpoint
- List substitutes for an order line
- File a fulfillment report
- Withdraw a fulfillment report
- Order created
- Order updated
- Order cancelled
- Order refunded
- Inventory adjusted
- Cancellation request created
- Cancellation request withdrawn
- Cancellation request resolved
- Order dispatch overdue
- Change request created
- Change request resolved
- Webhook test
- List an order's add-on requests (not yet available)
- Decline an add-on request (not yet available)
- List an order's add-on offers (not yet available)
- Send or replace an add-on offer (not yet available)
- Withdraw an add-on offer (not yet available)
- List the seller's shipping options
- Add-on request created (not yet available)
- Add-on request resolved (not yet available)
- Add-on offer updated (not yet available)