Skip to content

Error Handling

Anatomy of an error

All errors follow the response envelope:

{
"success": false,
"message": "Validation failed",
"data": null,
"errorCode": "VALIDATION_ERROR",
"errors": [ { "field": "msisdn", "message": "msisdn must be at least 10 characters" } ]
}

The errorCode string is stable across releases. Parse it in code — never parse message, which may change or be localized.


Retry strategy

StatusRetry?Strategy
4xx (except 408, 429)❌ NeverFix the request payload or auth.
408, 429✅ YesExponential backoff; respect Retry-After header.
5xx✅ YesUp to 3 retries, jittered backoff.
async function withRetry(fn, { max = 3 } = {}) {
for (let attempt = 1; attempt <= max; attempt++) {
const res = await fn();
if (res.ok) return res;
if (res.status < 500 && res.status !== 408 && res.status !== 429) return res;
const retryAfter = Number(res.headers.get('retry-after')) || 2 ** attempt;
await new Promise(r => setTimeout(r, retryAfter * 1000));
}
}

Checkout-specific errors

409 Out-of-stock during order creation

When a product runs out of stock between the customer viewing it and hitting Pay, the order endpoint returns:

{
"success": false,
"errorCode": "CONFLICT",
"message": "Insufficient stock for variant 22402",
"data": {
"type": "OutOfStockError",
"variant_id": 22402
}
}

Render a friendly “Sorry, this item sold out” message and re-fetch the product to show updated stock. Do not retry the order without showing the user updated availability.

400 Fulfillment input errors

MessageCauseFix
"only one recipient identifier is allowed"Two identifier keys in the same itemRemove the extra key
"this product expects email but received uid"Wrong identifier for this productUse GET /orders/fulfillment-fields/:variantId to get the right key
"Missing required fulfillment input: player_id"A required field was omittedAdd the missing key

400 Discount code errors

These errors are returned by both GET /api/v1/discounts/validate and POST /api/v1/orders when a discount_code is supplied.

MessageCauseFix
"Discount code "X" is not valid or has been deactivated."Code doesn’t exist or is_active=false.Show “invalid code” to the user.
"Discount code "X" is not active yet."Current date is before start_date.Inform the user when the code becomes valid.
"Discount code "X" has expired."Current date is after end_date.Ask the user to try a different code.
"Discount code "X" has reached its usage limit."All redemptions used up.Ask the user to try a different code.
"Your subtotal (X BDT) is below the minimum purchase (Y BDT)…"Cart total too small.Show the minimum purchase amount and ask the user to add more items.

Always validate the code with GET /api/v1/discounts/validate before allowing the user to submit the order, so the checkout form can show real-time feedback without waiting for an order creation error.


Preventing duplicate order submissions

If your checkout UI retries POST /api/v1/orders, use a client-side submission lock (disable the Pay button until the first request resolves) and reconcile using the returned order_number / transactionId before attempting a second create call. This is especially important around payment redirects and slow mobile connections.

See the full error code catalogue for codes you should special-case.