Error Handling
Error response shape
All error responses follow this structure:
{
"statusCode": 400,
"message": "text field must not exceed 4096 characters",
"error": "Bad Request"
}
Validation errors may return an array of messages:
{
"statusCode": 422,
"message": ["to must be a string", "text should not be empty"],
"error": "Unprocessable Entity"
}
Common errors
| Status | Cause | Fix |
|---|---|---|
400 | Missing required field or invalid format | Check request body against the schema |
401 | Missing or expired token / invalid API key | Re-authenticate; rotate the key |
403 | Resource belongs to a different tenant | Check the resource ID |
404 | Resource not found | Verify the ID exists |
409 | Duplicate idempotency key with different payload | Use a new key for different content |
422 | Semantic validation failure (e.g. bulk > 100 items) | Fix the payload |
429 | Rate limit exceeded | Back off; see Rate Limits → |
500 | Server error | Retry with exponential backoff; contact support if persistent |
Retry strategy
For transient errors (429, 500, 502, 503, 504):
async function withRetry(fn, maxAttempts = 3) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
if (attempt === maxAttempts - 1) throw err;
const retryable = [429, 500, 502, 503, 504].includes(err.status);
if (!retryable) throw err;
await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
}
}
}
For 409 (duplicate idempotency key), the original message is returned — this is not an error.