Appearance
Batch Commerce API
The Batch Commerce API is the customer-facing API of the Batch customer backend: the calls a storefront website makes on a shopper's behalf. These pages explain it by feature: sign-up and login, loyalty, shopping, payments, collectibles, content, and supporting services. Each operation lists its exact URLs, access requirements, request fields, an example, and response behavior. Examples use the first URL when several are listed; replace IDs and angle-bracket values with real data.
Every endpoint listed here takes the brand key. The same backend also serves the mobile app — documented separately in the Mobile API — and the Shopify plugin; those mounts, and routes with no brand-key check (provider webhooks, internal and event routes), are not listed. Six routes without a brand-key check stay because the web flows need them; each is marked No brand key on this route.
Prepared from the customer backend (batchsys-customer-backend) on 22 October 2026. This is a source-based reference; examples use fictional values and have not been sent to a running server. Database records, enabled integrations, and upstream services determine the available data. No production hostname is assumed.
Start here
- Obtain the server URL and credentials for your environment from the backend team. The local server listens on
PORT, defaulting to3000. - For a website, call
POST /api/v1/tenantwith its registeredOriginto load tenant details. A tenant is the brand/site context for the request. The returned site configuration includes the brand API key. A hub is a shop or fulfillment location; select it withPUT /api/v1/set-hubwhen needed, and use the returnedresult.apiKeyfor hub-specific calls. - Register or sign in using the sign-up and login section. Save
result.tokenfrom a successful login and send it asAuthorization: Bearer <CUSTOMER_TOKEN>on customer-protected routes. - Find the feature in the feature index and send the headers shown in its row. Prefix the path with your server URL.
- Check both the HTTP status and the JSON body. Many application errors return HTTP
200withsuccess: false.
bash
export BASE_URL='http://localhost:3000'
export ORIGIN='your-registered-site.example'
export API_KEY='<BRAND_OR_SELECTED_HUB_API_KEY>'
export CUSTOMER_TOKEN='<TOKEN_RETURNED_BY_LOGIN>'Use the origin registered for your site. The server normalizes Origin to its hostname plus optional port before validating the website API key; a browser can send a full origin such as https://your-registered-site.example. For local scripts, the hostname form above is also accepted by that middleware.
How to read request fields
| Term | Meaning |
|---|---|
| Path | Part of the URL. Replace :productId with an actual product ID, for example 123. |
| Query | Added after ?, for example ?limit=10&offset=0. URL-encode text and special characters. |
| Body | JSON sent with Content-Type: application/json. Most routes expect { "payload": { ... } }; exceptions are listed. |
body.payload.items[].id | The id property on each object in the items array inside payload. |
| Required: yes | Supply this field whenever its containing object/array is supplied. An optional parent does not become required because one of its children is required. |
| Required: conditional | A rule depends on another field or server context. Read the rule and operation notes. |
| Optional | The controller's Joi schema permits omission. Business logic can still require related data. |
| No controller schema | The field is read by the handler but is not type-validated there. Its upstream or database contract still applies. |
Fields and constraints below come from the controllers that actually execute. Files under app/*/validator mainly describe Swagger requests; the route engine does not run those files as validation middleware. Unknown keys are generally rejected by Joi object schemas unless .unknown(true) is used. Do not send fields from another operation's schema.
Headers and access
The endpoint tables use the following labels. Combine all labels in a row; for example, Origin + Brand key + Customer means all three are needed. JSON requests also need Content-Type: application/json. The labels describe the registered middleware, regardless of words such as user, brand, or open in a URL.
| Label | What to send / what it checks |
|---|---|
| Origin | Origin: <REGISTERED_SITE_ORIGIN>. Applied automatically to route-file endpoints unless they declare noCorsMiddleware or isInternal. The site must be on the server's whitelist. |
| Brand key | api-key: <BRAND_API_KEY>. The key's origin must match the normalized request origin; identifies the brand and organization. |
| Hub selected | Use the API key returned by hub selection. validHubApiKey checks that the existing tenant contains hubId; it does not read another header. |
| Customer | Authorization: Bearer <CUSTOMER_TOKEN>. Validates the customer JWT and loads customer context. |
Combinations seen on this site: Origin + Brand key, Origin + Brand key + Customer, the same two with Hub selected, and on the six flow routes Origin or Origin + Customer alone.
Responses, errors, and limits
Most v1 handlers return:
json
{
"success": true,
"result": { "message": "Operation completed" }
}result can be an object, array, scalar, or data from another service. Example response values in this guide illustrate the shape; they are not captured responses. Fields listed for a result are selected implementation details, not a promise that every response has the same fields.
The shared error handler usually returns HTTP 200:
json
{ "success": false, "error": "The reason the request failed" }Some early validation branches instead use status: false, message, or an error object such as { "message": "payload is required", "error": "Bad Request" }. Cloudinary uploads use status: true on success. Loyalty v2, mini-games v2, and several NFT endpoints forward another service's JSON directly. Do not add an assumed extra result wrapper.
| HTTP status | Meaning in this repository |
|---|---|
200 | Successful calls and many validation/business failures; inspect the body. |
400 | Some missing or invalid context, such as an unselected hub. |
401 | Authentication middleware rejected a credential. |
403 | Missing, unrecognized, or blocked origin: { "error": "Blocked by CORS" }. |
409 | Wallet ownership conflict; includes extra result data for the account-switch flow. |
413 | A file exceeds the upload limit. |
429 | Global rate limit exceeded. |
The global limit defaults to 15 requests per second per rate-limit key (IP by default), configurable with REQUEST_PER_SECOND. Rate-limit headers are enabled. File uploads use multipart form data with a 10 MiB limit per file. Use curl -F; let curl set the multipart boundary.
Pagination is operation-specific. limit generally controls page size and offset skips records; only use them where listed. Some controllers add pagination only when both values are truthy, so offset=0 may be ignored. Shopify order history uses limit and an opaque cursor instead. Array query values often use comma-separated strings; the operation notes identify conversions.
Common flows
The storefront's main journeys are playable step by step, each with a state diagram, on the web flows pages:
| Journey | Web flow |
|---|---|
| Every way in, and every way out | Authentication states |
| Register, then verify by OTP | Sign up |
| Password, OTP, magic link or Google | Log in |
| Recovery link, then a new password | Reset a password |
| Tenant, hub, catalogue, bag, order, payment | Shop to order |
| Verify a code, then claim or hold its reward | Scan to claim |
| Campaign progress, claim, spend at checkout | Earn and spend |
| Request, approve and transfer a scanned product | Ownership transfer |
Two shorter sequences have no flow page:
- Wallet switching: linking a wallet can return
409; send the wallet-switch OTP and verify it. Verification returns a customer token for the account that owns the wallet; use that token for subsequent requests. See Wallet linking. - Mini-games: create a session using
mini_game_id, submit a score using the returnedsession_id, then convert that session's score. Consuming points for lives is a separate operation. See Mini-games.
In the backend, the server entry is app.ts, the route loader engine/engineHelper.js, prefix configuration configServer.ts, the error handler app/v1/helpers/errorReporter.js, and the Swagger generator app/v1/helpers/generateSwaggerPathHelper.js. The running server exposes Swagger at /doc after initialization; these pages note the places where behavior differs from it.
Try the API from these pages
Every endpoint on these pages ends in a Try it bar. Open it for an editable request, copyable cURL / JavaScript / Python / Swift / Kotlin snippets, and a Send button that renders the response in place. The values below are shared with every console on the site and kept in this browser only. Use test-environment keys and tokens.
API console settings
Extra headers
Use this when the browser blocks the call with a CORS error.
npm run dev proxies /__api/* to BATCH_API_ORIGIN. Copied code snippets always keep the real host. Each console sends only the headers its access label calls for. Origin is the exception: browsers do not let a page set it, so a live call goes out with the docs site's own origin and the server's whitelist rejects it with 403. Either copy the cURL snippet, or run the docs with BATCH_API_ORIGIN=<host> npm run dev and tick the proxy option; the dev proxy then replaces Origin with the site origin entered above.
Feature index
Pages are grouped by what an endpoint does, matching the sidebar. Each operation is documented on exactly one page; where a route is served by a controller from another area — trending collectibles from the campaign controller — it is filed with the feature it returns, not the controller that serves it.
| Group | Section | Distinct endpoints | Operations |
|---|---|---|---|
| Accounts & identity | Sign-up and login | 10 | 10 |
| Customer profiles and accounts | 14 | 14 | |
| Government ID and age verification | 8 | 8 | |
| Notifications and preferences | 3 | 3 | |
| Wallet linking | 5 | 5 | |
| Catalogue & stores | Sites, shops, and locations | 8 | 8 |
| Products, catalogues, and search | 26 | 23 | |
| Batches and master data | 1 | 1 | |
| Cart, orders & payments | Cart and wishlist | 8 | 7 |
| Discounts and coupons | 9 | 7 | |
| Orders and checkout | 9 | 9 | |
| Payments and gateways | 11 | 11 | |
| Shopify rewards and gift cards | 5 | 5 | |
| Loyalty programme | Loyalty points, levels, and rewards | 18 | 17 |
| Loyalty campaigns | 11 | 11 | |
| Referrals | 3 | 3 | |
| Mini-games | 7 | 7 | |
| Authenticity & collectibles | Product scanning and authenticity | 9 | 8 |
| Receipts | 3 | 3 | |
| Collectibles | 5 | 5 | |
| Product ownership | 4 | 4 | |
| NFTs and reward fulfillment | 10 | 10 | |
| Content & engagement | Banners and magazines | 4 | 4 |
| Reviews and ratings | 16 | 15 | |
| Forms and events | 8 | 5 | |
| Newsletters | 2 | 2 | |
| Platform services | Uploads and analytics | 5 | 3 |
The section pages cover 222 endpoints in 208 controller operations.
Duplicate registrations, commented-out routes and other implementation gaps are on Implementation notes.