Appearance
Mobile API overview
The mobile APIs are the routes the Batch mobile apps call — every route in the customer backend whose endPoint contains mobile/: 166 unique method + path pairs across 150 handlers. The web storefront calls the non-mobile twins of most of them.
Routes are declared in routes/v1/ and routes/v2/ of the customer backend and mounted at boot by engine/engineHelper.js; the prefixes come from routePrefixV1 in configServer.ts. Several handlers are mounted at both a guest and an authenticated path — see Duplicates, aliases and quirks.
Base URL & prefixes
| routePrefix key | Prefix |
|---|---|
openRoute | /api/v1 |
userRoute | /api/v1/user |
brandRoute | /api/v1/brand |
brandRouteV2 | /api/v2/brand |
/api/v2/ is not a second, different API. It marks the routes the customer backend serves by calling out to a standalone service — the content service behind CMS content, the points ledger behind points and mini-games. The request and response conventions below apply to them too, with one exception noted there: a proxied body comes back unenveloped.
The prefix is not an auth boundary on mobile routes — auth is decided purely by the middleware list. /api/v1/mobile/..., /api/v1/user/mobile/... and /api/v1/brand/mobile/... all exist, and some authenticated endpoints live under /api/v1/mobile/. Always use the exact path given on the endpoint pages.
Headers
| Header | When | Value |
|---|---|---|
x-auth-token | Every mobile route | Bearer <brand mobile key JWT> |
authorization | Routes marked Customer | Bearer <customer JWT> |
Content-Type | Requests with a JSON body | application/json |
x-auth-tokenis verified byisValidMobileKeyagainstBRAND_MOBILE_KEY. The JWT payload must contain bothbrandIdandhubIdor the request is rejected with 401. The middleware then loads the brand and puts{ brandId, hubId, org_id, type, loyaltyBrandId, isMobileSource: true }onreq.tenant.authorizationis verified byisCustomeragainstJSON_CUSTOMER, and puts{ userId, level_id, ... }onreq.meta.- Mobile routes use
noCorsMiddleware— nooriginheader is required (unlike the web routes). Docstrings in the source that say "requires api-key and origin" are copy-paste leftovers from the web route; ignore them. brandIdandhubIdalways come from the token, never from the client — except where ahubId/roomIdquery param is explicitly listed on an endpoint page as an override.
Request body shape
Almost every POST/PUT wraps its body in a payload key:
json
{ "payload": { "field": "value" } }If payload is missing, most handlers return { success: false, error: { message: "payload is required" } }.
Exactly three endpoints take an unwrapped body: loyalty/milestone/redeem (sends collectibles), campaigns/almost-completed (sends event_item) and user/mobile/preference (sends a bare array). Everything else wraps — including trade replies and every /api/v2/ route, mini-games included.
Response shape
Success:
json
{ "success": true, "result": { } }Failure (see handleCatch):
json
{ "success": false, "error": "email must be a valid email" }⚠️ handleCatch defaults to HTTP 200. A failed request usually still returns 200 OK with success: false. Clients must branch on the success field, not the status code. Exceptions: auth middleware returns 401, and GET /api/v2/brand/mobile/cms returns 404 when the brand has no published content.
Joi validation failures surface as the first failing message only, with error: "INVALID PAYLOAD: Check Payload Arguments" in some paths.
Conventions used in these tables
- req —
✅required,—optional. - Comma-separated query params (
tags,category,group_by,sortItem,sortType) are split on,server-side; senda,b,c. limit/offsetare offset pagination. Where both must be supplied together it is noted (.and("limit","offset")).- Unknown keys are rejected by Joi (
.keys()is strict by default) — don't send extra fields. - Phone numbers:
phoneNumberis stripped to digits server-side, andphone_prefixbecomes required wheneverphoneNumberis present. - Emails containing
+are rejected on all customer-identity endpoints.
Try an endpoint from the docs
Every endpoint on these pages carries a console: a collapsed bar under the endpoint's description that opens into a request editor, five copy-ready code snippets (cURL, JavaScript, Python, Swift, Kotlin) and a Send button whose response renders in the same block.
Fill the two tokens in once below and every console on every page picks them up — they are stored in this browser's localStorage and are never sent anywhere but the host you set. Use test-account 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 x-auth-token on every request, adds authorization on the endpoints marked Customer, and shows the exact header list before you send. Path parameters, query parameters and the JSON body are editable, and the code snippets regenerate from whatever is in the editor.
The browser may block the call
A Send from this page is a cross-origin request, so it only completes if the customer backend returns CORS headers to the docs origin. If it fails with "Request failed", the call was blocked before the server answered — copy the cURL tab and run it from a terminal, or run the docs locally with a relay:
bash
BATCH_API_ORIGIN=https://your-customer-backend npm run devthen tick send through a local proxy path in the settings above. The dev server relays /__api/* server-side, where CORS does not apply. A built static site has no relay.
Endpoint reference
| Page | Covers |
|---|---|
| Auth, account and addresses | register, login, OTP, OAuth, password reset, profile, merge, deactivate, addresses |
| Product authentication | authcode/NFC verify, claim points and collectibles, hold/release, OCR |
| Loyalty and campaigns | points, transactions, scans, referrals, the points ledger service, campaigns and progress, mini-games |
| Collectibles, trades and ownership | owned and catalogue collectibles, leaderboards, trades, product ownership transfer |
| Catalog, search and products | tag/related/filter catalogues, Typesense search, product and strain lookup, master data |
| Cart, discounts, orders and payments | bag sync, discounts, guest and customer orders, order history, payment gateways |
| Reviews, wishlist and receipts | product reviews (two systems), wishlist, receipt upload and status counts |
| Content, forms and contact | CMS content, blogs, events and leads, forms, store locator, contact-us, newsletter |
| Gift cards, notifications and uploads | Shopify gift cards, Tremendous rewards, push tokens, consent flags, notifications, image upload |
| Enum reference | every accepted enum value, and whether to send the key or the value |
| Duplicates, aliases and quirks | shared handlers, duplicate declarations, route-ordering hazards, gotchas |
Legend for the Auth column used throughout: Brand = x-auth-token only · Customer = x-auth-token + authorization.