shopId or domain. Responses are cached on the CDN, and CORS allows requests from shop domains and preview addresses.
Endpoints
Base URL:https://itemshop.dev/api/v2/storefront/v1
Example: product list
Checkout
Response
- A cart is 1–20 lines in
items; more is400 CART_TOO_LARGE. One line is a regular order. Two or more form a payment group: one transaction at the provider for the total, with a separate order per line inside. The response then hasgroupIdandorderIds[], andorderIdandorderNumberpoint to the group. A discount code is applied once for the whole cart, and a payment method’s min/max limits are checked against the total. - Nick or e-mail: a
gameshop requiresnick(3–16 charactersa-zA-Z0-9_, otherwiseNICK_REQUIRED); a general shop requiresemail(otherwiseEMAIL_REQUIRED). A product with digital delivery (deliveryKindother thancommands) always requiresemail, even in agameshop. - Consents are required:
terms(the terms) andimmediateDelivery(a combined consent to immediate delivery and loss of the right of withdrawal), pluslegalVersion— the version of the documents fromGET /shops/:shopIdOrDomainthat you showed the buyer. If the shop changes its terms, checkout returnsLEGAL_DOCUMENTS_CHANGED— fetch the shop again and show the new documents. Idempotency-Key(e.g. a UUID) is required. Retrying with the same key won’t create a second order.returnUrlmust point to the shop’s domain or a preview address (otherwiseRETURN_URL_NOT_ALLOWED). After payment the buyer returns to it with?itemshop_order=<number>&itemshop_key=<status key>(any#…fragment is dropped) — read them fromlocation.searchand fetch the status by number and key. In productionlocalhost,127.0.0.1and[::1]are rejected (also asOrigin). WithoutreturnUrlthe buyer lands on the shop’s hosted status page.- A free order (100% discount) has
free: trueand aredirectUrlthat leads straight to the status page. - The payment method is the
codefrom/payment-methods— a shop can have several methods from the same provider. - Bot protection is adaptive: it doesn’t block regular purchases, and checkout is also protected by a rate limit and duplicate-request blocking.
- The site does not process payments — it only redirects to
redirectUrl.
Errors
Every error has the same envelope with a machine-readablecode — translate the message in your UI based on it; the error field is for the developer only.
Redeeming a voucher
POST /shops/:shopId/vouchers/redeem with { code, nick } and an optional email (an address for digital content; also turnstileToken or the cf-turnstile-response header) returns { orderId, orderNumber, statusKey, status, productName, serverName, nick }. A voucher creates a free order delivered like a normal purchase.
Order status
GET /orders/by-number/:orderNumber/status?key=<statusKey> returns { orderId, orderNumber, shopId, status, productName, serverName, nick, amount, currency, createdAt, groupId, items[], delivery }. The number can point to an order or a group; for a group status is aggregated (it can be partially_refunded) and items[] lists the lines (orderId, orderNumber, status, productName, variant, quantity, serverName, amount). A wrong or missing key is 404 ORDER_NOT_FOUND. In a general shop nick is a masked e-mail. Orders created since 3.0 have a 10-character statusKey — it comes back from checkout and voucher redemption and in the return URL. The old GET /orders/:orderId/status works only for pre-3.0 orders.
Collecting digital content
Whendelivery.ready in the status is true, fetch the content: GET /orders/by-number/:orderNumber/delivery?key=<statusKey> (20/min/IP, no-store). The response is { orderNumber, items[] }; a line has kind (license_keys, download, text), state (pending, awaiting_stock, delivered, revoked), keys[], pendingUnits, text and download { url, fileName, expiresAt, maxDownloads, remaining, expired }. Render text as plain text, never HTML. awaiting_stock with pendingUnits > 0 means the key pool ran dry — keys also follow by e-mail. Each opening of download.url (GET /downloads/:token) uses one download. An unpaid order is 409 ORDER_NOT_PAID, a refund or dispute 410 DELIVERY_REVOKED. Seller-side setup: Digital content delivery.
Widgets and privacy
In the shop settings, the owner can turn off public data (recent purchases, top spenders, monthly goal, online players). A disabled widget returns an empty list for everyone — the site should then hide the section.Cache and limits
- Catalog data is cached on the CDN for a short time — a price change in the panel shows up on the site after a moment.
- Checkout and vouchers have a per-IP request limit. See Errors and limits.
TypeScript SDK
A convenient layer over this API.
API with a key
Managing the shop from a server.