Spalce uses conventional HTTP status codes to indicate success or failure. Codes in the 2xx range mean success. Codes in the 4xx range indicate a problem with the request — your code should not retry these automatically. Codes in the 5xx range indicate a problem on our side and are safe to retry with backoff.
Error envelope
Every error response shares the same shape. The type field is the broad error family. The code field is a stable string you can branch on. The message is a human-readable description suitable for logs but never for end-user display. The doc_url points to a page with troubleshooting steps.
{
"error": {
"type": "invalid_request_error",
"code": "parameter_missing",
"message": "Missing required parameter: customer",
"param": "customer",
"doc_url": "https://docs.spalce.dev/errors/parameter_missing",
"request_id": "req_01HEC2P4N9WSRYBT3K8MXJZDFV"
}
}Common status codes
- 400 Bad Request — your request was malformed or violated a schema constraint.
- 401 Unauthorized — credential missing, malformed, or revoked.
- 403 Forbidden — credential valid but missing the required scope.
- 404 Not Found — the resource does not exist (or you cannot see it).
- 409 Conflict — idempotency key reused with a different body, or version mismatch.
- 422 Unprocessable Entity — payload shape is valid but the business rule failed.
- 429 Too Many Requests — rate limit exceeded. Honor the Retry-After header.
- 500 Internal Server Error — something is wrong on our side. Retry with backoff.
- 503 Service Unavailable — a dependency is degraded. Retry with backoff.
Every error response carries a request_id. Include it in support tickets and we can find the trace in under a minute.
What to retry, and how
4xx responses should not be retried without changing the request — you will just hit the same error. 5xx and 429 should be retried with exponential backoff and jitter. We recommend an initial delay of 250ms, a maximum delay of 30s, and a cap of six attempts. Always combine retries with idempotency keys so a transient failure cannot create a duplicate resource.
async function withRetry<T>(fn: () => Promise<T>, attempts = 6): Promise<T> {
let delay = 250;
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1 || !isRetryable(err)) throw err;
await sleep(delay + Math.random() * delay);
delay = Math.min(delay * 2, 30_000);
}
}
throw new Error("unreachable");
}Was this article helpful?
