Error Handling
Common errors and how to fix them
HTTP Status Codes
| Code | Meaning | Action |
|------|---------|--------|
| **200** | Success | Order created, check jobId |
| **400** | Validation Error | Fix request body, retry immediately |
| **401** | Authentication Failed | Check token, get new one if expired |
| **403** | Forbidden / Revoked / Expired | Check error message, may need new key |
| **429** | Rate Limited | Wait (see Retry-After header), retry |
| **500** | Server Error | Retry with exponential backoff |
<h2>Validation Errors (400)</h2>
<h3>Response Format</h3>
```json
{
"success": false,
"error": "Validation failed",
"details": [
{
"field": "items[0].quantity",
"message": "must be greater than 0",
"value": 0
},
{
"field": "customerId",
"message": "required field",
"value": null
}
]
}
```
The `details` array contains all validation errors found in a single request. The `value` field shows what was submitted (omitted if null).
<h3>Common Validation Errors</h3>
**Missing Required Field**
- One of: `customerId`, `supplierId`, `locationId`, `agentId` must be provided
- `items` array (must have at least 1 item)
- Each item must have: `sku`, `productName`, `quantity` > 0
**Invalid Values**
- `quantity` must be greater than 0 (decimals allowed)
- `orderType` must be one of: send, receive, return, transfer
- Multiple contact fields provided (only one allowed)
**Fix:** Review the `details` array, fix the fields shown, and retry.
<h3>Multi-business key</h3>
| Error Message | Cause | Fix |
|---|---|---|
| `businessId required` | Multi-business API key used without `X-Business-ID` | Send `X-Business-ID` with one of the businesses bound to the key. Classic single-business keys never need this header. |
<h2>Authentication Errors (401)</h2>
<p>All 401 errors share the same response format:</p>
```json
{
"error": "<error message>"
}
```
<h3>All Possible 401 Errors</h3>
| Error Message | Cause | Fix |
|---|---|---|
| `missing or invalid Authorization header` | No Bearer header or malformed | Include `Authorization: Bearer <token>` header |
| `invalid token` | JWT parsing/verification failed | Get a new token |
| `token not valid` | JWT validation check failed | Refresh your token, it may have expired |
| `invalid token claims` | JWT claims malformed | Get a new token |
| `token issuer mismatch` | Token issued by wrong auth provider | Ensure token is from EQUOS |
| `token audience mismatch` | Token audience is not `https://api.equos.one` | Check token request uses correct audience |
| `token missing sub claim` | No subject (client_id) in token | Verify your API key is configured correctly |
| `unknown application` | Token subject (client_id) not found in system | Verify the API key exists and is active |
<h2>Permission Errors (403)</h2>
<p>Response format:</p>
```json
{
"error": "<error message>"
}
```
<h3>Possible 403 Errors</h3>
| Error Message | Cause | Fix |
|---|---|---|
| `api key revoked` | API key was revoked via admin console | Create a new API key in Equos admin console |
| `api key expired` | API key expiration date has passed | Create a new API key with a future expiry date |
| `insufficient scope: orders:write required` | Token scope doesn't include `orders:write` | Create a new API key with `orders:write` scope |
| `not authorized for business` | Admin API (`/api/keys`) requires the business owner or a super admin | Sign in as the business owner |
| `key not bound to business` | Multi-business key sent `X-Business-ID` for a tenant it is not bound to | Use a business id from the set selected when the key was minted |
<h2>Rate Limiting (429)</h2>
<p><strong>Limit:</strong> 1 request/second per API key, applied to all <code>/e9v3</code> endpoints including reads, with a burst capacity of 10. (The 429 body's <code>rate_limit</code> field reads <code>"1 order/second"</code> verbatim — that is the literal server string.)</p>
<p><strong>Response format:</strong></p>
```json
{
"error": "rate limit exceeded",
"retry_after": 0.82,
"rate_limit": "1 order/second",
"burst_capacity": 10
}
```
<p><strong>Response headers:</strong></p>
- `Retry-After: 1` (seconds until next token available)
- `X-RateLimit-Limit: 1` (orders per second)
- `X-RateLimit-Remaining: 0` (tokens available now)
- `X-RateLimit-Reset: 1234567890` (unix timestamp of next token)
<p><strong>Fix:</strong> Wait the time specified in `Retry-After` header or `X-RateLimit-Reset` timestamp, then retry. See <a href="/developer-docs/guides/rate-limiting">Rate Limiting</a> for best practices.</p>
<h2>Server Errors (500)</h2>
<p><strong>Response format:</strong></p>
```json
{
"success": false,
"error": "Failed to queue order processing"
}
```
<p><strong>Cause:</strong> Temporary server issue (system queue issue, processing delay, etc.).</p>
<p><strong>Fix:</strong> Retry with exponential backoff (2s, 4s, 8s, etc.). If the error persists, contact support.</p>
<h2>Best Practices</h2>
<h3>✓ Do This</h3>
<ul>
<li>Check HTTP status code before processing response body</li>
<li>Parse the `details` array in 400 responses to show users which fields are invalid</li>
<li>Respect `Retry-After` header or `X-RateLimit-Reset` timestamp for 429 errors</li>
<li>Use exponential backoff for 5xx errors (start with 2 seconds, double each retry, max 3-5 attempts)</li>
<li>Log full error details including request ID, timestamp, and response body</li>
<li>Display validation errors to end users so they can correct input</li>
</ul>
<h3>✗ Don't Do This</h3>
<ul>
<li>Retry 400 errors (validation errors won't succeed without fixing the request)</li>
<li>Retry 401/403 errors without fixing the auth issue (getting a new token, API key, etc.)</li>
<li>Ignore 429 errors and retry immediately (you'll hit the limit again)</li>
<li>Retry without any delay (hammers the server and wastes resources)</li>
<li>Display raw error messages to non-technical users</li>
</ul>