Skip to main content

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

StatusCauseFix
400Missing required field or invalid formatCheck request body against the schema
401Missing or expired token / invalid API keyRe-authenticate; rotate the key
403Resource belongs to a different tenantCheck the resource ID
404Resource not foundVerify the ID exists
409Duplicate idempotency key with different payloadUse a new key for different content
422Semantic validation failure (e.g. bulk > 100 items)Fix the payload
429Rate limit exceededBack off; see Rate Limits →
500Server errorRetry 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.