Errors
Shadowfax APIs do not share a single error envelope. The shape you get back depends on which endpoint you called, so build your error handling per integration rather than assuming one format platform-wide.
The shapes below are those returned by Forward, Reverse, Exchange, and Hyperlocal. Quick Commerce uses its own envelope.
The one field you can rely on almost everywhere is message. Endpoints that predate the current gateway return responseCode/responseMsg instead.
A 200 does not always mean success
Forward Logistics order creation returns HTTP 200 for failures, including missing required fields, unserviceable pincodes, and duplicate client_order_id. The failure is signalled in the body, not the status line:
Code
If you branch on status_code == 200 or response.ok, you will record failed orders as created. On Forward, always check message — "Success" means the order exists and is under data; "Failure" means it does not.
Reverse Logistics behaves differently: the same class of validation error returns HTTP 400, and errors is an object keyed by field rather than a string.
| Forward Logistics | Reverse Logistics | |
|---|---|---|
| Validation failure status | 200 | 400 |
errors type | string | object keyed by field |
| Duplicate order | message: "Failure", AWB in AWB | existing order returned in the normal {message, data, errors} shape, AWB in data.awb_number |
Don't carry assumptions from one product to the other.
Error Shapes
message with validation detail
Returned by order creation across Reverse, Forward, and Exchange. message is "Failure" and errors carries the detail.
Code
errors is not always an object. When an order is rejected by a business rule rather than by field validation, it is a plain string:
Code
Handle both forms. There is no machine-readable error code on these responses — order creation returns no failure_type, failure_code, or equivalent. Branch on the HTTP status to decide whether the call failed, and parse errors only when you need to tell one cause from another. Match on errors loosely: the strings are not a stable contract and business-rule messages are configured per account.
message only
The most common shape. Used by AWB generation, bulk tracking, order update, proof of delivery, and both Hyperlocal APIs.
Code
responseCode / responseMsg
Used by the older endpoints — cancellation and single-order tracking.
Code
responseCode repeats the HTTP status code.
status / errorCode / message
Returned on authentication failure by the V4 tracking endpoints.
Code
Status Codes by Product
Only the codes below are documented per product. Treat anything else as an unexpected response and fail safe.
| Product | Documented codes |
|---|---|
| Reverse Logistics | 400, 401, 403 |
| Forward Logistics | 400, 401, 403 |
| Exchange | 400, 401 |
| Hyperlocal Marketplace | 400, 401, 500 |
| Hyperlocal Dedicated Store | 400, 401, 500 |
| Quick Commerce | 400, 401, 404, 409 |
| Code | Meaning |
|---|---|
400 | Bad request — missing or invalid parameters, or a business rule rejection |
401 | Authentication failed — missing, invalid, or expired token |
403 | Forbidden — token is valid but lacks permission. On Reverse this is also how an account that hasn't finished onboarding is reported |
404 | Resource not found — Quick Commerce only |
409 | Conflict — duplicate order or disallowed state transition. Quick Commerce only |
429 | Throttled — back off and retry. See below |
500 | Server error — retry with exponential backoff |
429 is not listed per product, but you will hit it
None of the specs declare a 429, but the gateway does return one, using the responseCode/responseMsg envelope:
Code
Throttling is applied more aggressively than the published per-minute guidance suggests, particularly on tracking. Treat 429 as expected and back off — don't treat it as a bug. See Rate Limits.
Quick Commerce maintains a fuller error-code list that isn't published here — request it from your integration POC.
Common Errors
| Error | Cause | Fix |
|---|---|---|
"Authentication credentials were not provided." | Missing Authorization header | See Authentication |
"Invalid or expired authentication token" | Token wrong, or an OAuth2 token past its expiry | Re-issue the token; expiry varies by product |
"Duplicate client_order_id" | An order with this ID already exists | Order creation is idempotent inside the window below — the existing order is returned rather than a duplicate created |
"Invalid AWB Number" | AWB doesn't exist or belongs to another client | Confirm the AWB came back from order creation |
Retrying
- Retry
500responses with exponential backoff, starting at 1s. - Do not blind-retry
400— the request needs changing first. - Order creation is idempotent on
client_order_id, but only for a limited window — 30 minutes on Reverse, 10 minutes on Exchange. Retrying a create you never got a response to is safe inside that window.
Past the window, the same client_order_id creates a second, genuine order with its own AWB. It is not rejected as a duplicate. A delayed retry, or a replayed message from your own queue, will therefore double-book a real pickup or delivery. If you need to re-send after the window, check the order's status first — see Pickup Orders for the Reverse case.