Error codes
The API never returns localized text. Every failure is a JSON envelope with a stable machine-readable code; you map that code to your own strings.
The envelope
statusCode repeats the HTTP status, code is the stable identifier, message is an English developer hint that may change at any time, and details carries structured context for some codes (for example which items were out of stock).
{
"statusCode": 409,
"code": "ERR_OUT_OF_STOCK",
"message": "insufficient stock for 2 items",
"details": { "items": [{ "productId": "…", "requested": 5, "available": 2 }] }
}Match on code, never on message
message is written for a human reading a log. It is not part of the contract and is not translated; code is.
The catalogue
97 codes
General
| Code | Meaning |
|---|---|
ERR_VALIDATION | The request body or query failed validation; details lists the offending fields. |
ERR_BAD_REQUEST | The request was malformed in a way validation could not describe. |
ERR_UNAUTHORIZED | No credential was sent, or the access token has expired. |
ERR_FORBIDDEN | Authenticated, but not allowed to perform this action. |
ERR_NOT_FOUND | The resource does not exist, or is not visible to you. |
ERR_CONFLICT | The write conflicted with concurrent activity; retry the request. |
ERR_RATE_LIMITED | Too many requests for this key. Wait the number of seconds in Retry-After. |
ERR_INTERNAL | An unexpected server error. Safe to retry with backoff. |
ERR_NETWORK | The request never reached the API (network, DNS or CORS). |
ERR_UNKNOWN | An unrecognised error; check the raw response. |
Authentication and accounts
| Code | Meaning |
|---|---|
ERR_OTP_INVALID | The one-time code does not match. |
ERR_OTP_EXPIRED | The one-time code has expired; request a new one. |
ERR_OTP_RATE_LIMITED | Too many code requests for this phone number or email address. |
ERR_OTP_TOO_MANY_ATTEMPTS | Too many wrong codes; request a new one. |
ERR_USER_BLOCKED | The account is blocked; every token for it is refused. |
ERR_STAFF_PHONE_EXISTS | A user with that phone number already exists. |
ERR_PHONE_REQUIRED | This account has no contact phone number — add one before placing the order. |
ERR_CONTACT_TAKEN | That phone number or email address already belongs to another account. |
ERR_CHANNEL_DISABLED | That sign-in method is switched off here — use the other one. |
Partner API, keys and webhooks
| Code | Meaning |
|---|---|
ERR_API_KEY_INVALID | The X-Api-Key header is missing or does not match any key. |
ERR_API_KEY_REVOKED | The key was revoked, or its rotation grace period has ended. |
ERR_API_KEY_SCOPE | The key is valid but does not carry the scope this endpoint needs. |
ERR_API_KEY_ENVIRONMENT | A sandbox key was sent to the live API, or the other way round. |
ERR_API_KEY_LIMIT | You already hold the maximum number of active keys. |
ERR_WEBHOOK_URL_INVALID | The endpoint URL must be https (http is allowed only for localhost outside production). |
ERR_WEBHOOK_LIMIT | This key already has the maximum number of webhooks. |
ERR_WEBHOOK_DISABLED | This webhook is disabled — re-enable it before sending a test delivery. |
ERR_PARTNER_ORDER_NOT_ASSIGNED | The order is not assigned to your partner account. |
Payments
| Code | Meaning |
|---|---|
ERR_PAYMENT_METHOD_UNAVAILABLE | That payment method is disabled in this environment. |
ERR_PAYMENT_PROVIDER_UNAVAILABLE | The card provider is not configured or did not answer. |
ERR_PAYMENT_INTENT_INVALID_STATE | The payment intent is not in a status that allows this action. |
ERR_PAYMENT_SIGNATURE_INVALID | The provider webhook signature did not verify. |
ERR_PAYMENT_ALREADY_PAID | The order has already been paid. |
Orders
| Code | Meaning |
|---|---|
ERR_NO_DEFAULT_ADDRESS | The customer has no default delivery address. |
ERR_OUT_OF_STOCK | One or more lines exceed available stock; details lists them. |
ERR_PRODUCT_UNAVAILABLE | A product in the order is no longer purchasable. |
ERR_OUT_OF_ZONE | The delivery address falls outside every active delivery zone. |
ERR_ORDER_NOT_CANCELLABLE | The order has moved past the point where it can be cancelled. |
ERR_ORDER_NOT_RATABLE | Only a delivered order can be rated. |
ERR_ALREADY_RATED | This order already carries a rating. |
ERR_INVALID_TRANSITION | The order state machine refuses this transition from the current status. |
ERR_IDEMPOTENCY_KEY_REQUIRED | This write needs an Idempotency-Key header. |
ERR_IDEMPOTENCY_CONFLICT | The same Idempotency-Key was reused with a different body. |
ERR_ORDER_NOT_ACCEPTABLE | The order is not in a status a vendor can accept or reject. |
ERR_ORDER_ALREADY_CLAIMED | Another runner has already claimed this order. |
ERR_ORDER_NOT_CLAIMED | The order is not claimed by you. |
ERR_ORDER_NOT_PACKABLE | The order is not in a state that can be packed, re-packed or printed. |
Catalog and vendors
| Code | Meaning |
|---|---|
ERR_VENDOR_NOT_FOUND | No vendor profile for this user or id. |
ERR_ITEMS_ALREADY_DECIDED | These order items were already accepted or rejected. |
ERR_PRODUCT_NOT_OWNED | The product belongs to a different vendor. |
ERR_CATEGORY_INVALID | The category id is unknown or not a leaf category. |
ERR_SKU_CONFLICT | The generated SKU collided with an existing one. |
ERR_MEDIA_UNAVAILABLE | Object storage is not configured or not reachable. |
ERR_MEDIA_INVALID | The upload was rejected: wrong content type or too large. |
ERR_CATEGORY_NOT_EMPTY | The category still has products or child categories. |
ERR_CATEGORY_CYCLE | The move would make a category its own ancestor. |
Dispatch and delivery
| Code | Meaning |
|---|---|
ERR_PICKUP_INCOMPLETE | Not every accepted item has been picked yet. |
ERR_ITEM_NOT_PICKABLE | This item is not in a status that can be picked. |
ERR_DRIVER_NOT_FOUND | No driver profile for this user. |
ERR_DRIVER_OFFLINE | The driver is not online, so no batch can be offered. |
ERR_DRIVER_BUSY | The driver already has an active batch. |
ERR_BATCH_NOT_OFFERED | The batch is not currently offered to this driver. |
ERR_OFFER_EXPIRED | The 90-second accept window has passed. |
ERR_BATCH_NOT_ASSIGNED | The batch is not assigned to this driver. |
ERR_SCAN_INCOMPLETE | The scan gate refused departure: not every item is scanned. |
ERR_SCAN_WRONG_BATCH | That barcode belongs to an item in another batch. |
ERR_STOP_ALREADY_RESOLVED | The stop is already delivered or failed. |
ERR_BATCH_INCOMPLETE | The batch still has unresolved stops. |
ERR_COD_VARIANCE_NOTE_REQUIRED | Collected cash differs from the expected total; a note is required. |
ERR_BATCH_INVALID_TRANSITION | The batch state machine refuses this transition. |
ERR_ROUTE_UNAVAILABLE | The routing provider failed and the fallback could not build a route. |
ERR_DELIVERY_RETRY_LIMIT | This order has already been retried the maximum number of times. |
ERR_ITEM_NOT_EXPECTED | The scanned line is not one the hub should receive: its order was cancelled, or the vendor rejected it. |
ERR_INTAKE_INCOMPLETE | Intake cannot be completed yet — some accepted lines of the order have not been scanned in at the hub. |
ERR_PARCEL_NOT_IN_BATCH | That parcel code belongs to no order in the batch being loaded. |
ERR_WRONG_STOP_PARCEL | The parcel scanned at the door belongs to another stop of the route; it is never recorded as a delivery. |
ERR_PARCEL_NOT_LOADED | No van scan ever put this parcel on this batch, so it cannot be handed over at the door. |
ERR_OUTSIDE_GEOFENCE | The position is further from the stop’s address than the delivery geofence allows, and no override reason was given. |
ERR_DELIVERY_SCAN_INCOMPLETE | Every parcel of the stop must be scanned at the door before it can be marked delivered. |
ERR_LOCATION_REQUIRED | The route cannot start without a recent online driver-presence heartbeat. |
Support, refunds and returns
| Code | Meaning |
|---|---|
ERR_TICKET_CLOSED | The support ticket is closed and cannot receive new messages. |
ERR_TICKET_NOT_ASSIGNABLE | The ticket cannot be assigned to that agent. |
ERR_CSAT_NOT_ALLOWED | A satisfaction score can only be left on a resolved or closed ticket. |
ERR_CSAT_ALREADY_GIVEN | This ticket already has a satisfaction score. |
ERR_REFUND_AMOUNT_EXCEEDS | The refund would exceed what was paid or already refunded on the order. |
ERR_REFUND_INVALID_STATE | The refund is not in a status that allows this action. |
ERR_REFUND_ORDER_NOT_ELIGIBLE | The order cannot be refunded in its current status. |
ERR_RETURN_NOT_ELIGIBLE | The order cannot be returned in its current status. |
Platform administration
| Code | Meaning |
|---|---|
ERR_DEPENDENCY_UNAVAILABLE | A backing service (Redis) was unreachable — nothing was applied. Safe to retry with backoff. |
ERR_ZONE_INVALID_POLYGON | The delivery zone polygon is not a valid closed ring. |
ERR_SETTLEMENT_EXISTS | A settlement already exists for this vendor and period. |
ERR_SETTLEMENT_INVALID_STATE | The settlement is not in a status that allows this action. |
ERR_SETTLEMENT_PERIOD_OPEN | The settlement period has not ended yet. |
ERR_SETTING_UNKNOWN | No platform setting with that key. |
ERR_SETTING_INVALID | The setting value is outside the allowed range or type. |
ERR_COUPON_INVALID | The coupon code does not apply — unknown, expired, not yet started, over its redemption limit, or below its minimum order amount. |
ERR_COUPON_CODE_TAKEN | A coupon with this code already exists; codes are unique. |