4xx and 5xx responses, so you can write one error handler that works across every endpoint.
The error envelope
Every error response has the same JSON shape:The error object describing what went wrong.
A unique identifier for this request, at the top level of the response (a sibling of
error, not nested inside it). Give this to support when reporting a problem.X-Request-Id
Every API response — successful or not — includes anX-Request-Id response header. Its value is the same identifier returned as request_id in the error envelope.
These are two surfaces of the same id:
X-Request-Idheader — present on every response, including2xxsuccesses and204 No Contentresponses that have no body.request_idbody field — present in every error envelope.
401 Unauthorized responses also include a WWW-Authenticate: Bearer header.Error types
error.type maps directly to the HTTP status code. The table lists every type, its status, the codes that can appear under it, and what it means.
error.type | HTTP status | error.code values | Meaning |
|---|---|---|---|
invalid_request_error | 400 | invalid_json, missing_parameter, invalid_parameter, payload_too_large, invalid_cursor | The request was malformed: unparseable JSON, a missing or invalid parameter, a body that was too large, or a bad/expired pagination cursor. |
authentication_error | 401 | missing_api_key, invalid_api_key, revoked_api_key, expired_api_key | The API key is missing, unrecognized, revoked, or expired. Response includes WWW-Authenticate: Bearer. |
permission_error | 403 | plan_required, insufficient_scope | The key is valid but not allowed: the plan does not include API access (plan_required), or the key lacks the required scope (insufficient_scope). |
not_found_error | 404 | resource_not_found | The requested resource does not exist or is not accessible by this key. |
conflict_error | 409 | — | The request conflicts with the current state of the resource. |
validation_error | 422 | validation_failed | The request was well-formed but semantically invalid — for example, invalid BPMN 2.0. |
rate_limit_error | 429 | rate_limit_exceeded, quota_exceeded | A limit was hit: the per-key request burst limit (rate_limit_exceeded) or the monthly AI generation quota (quota_exceeded). Response includes Retry-After. |
api_error | 500 | internal_error | Something went wrong on our side. Retry later and, if it persists, contact support with the request_id. |
The
Generation resource has its own job-level error object ({ code, message }) that describes why an async generation failed. That is part of a successful 200/202 poll response — it is not the error envelope described on this page.Example error response
A422 returned when importing invalid BPMN 2.0 to POST /diagrams/import. Note error.param pinpointing the offending field, and request_id at the top level.
Handling errors
Using request_id for support
When something goes wrong and you need help, therequest_id lets us locate the exact request in our logs.
Capture the id
Read
X-Request-Id from the response header (always present), or request_id from the error body. They are the same value.Log it alongside the error
Store the
request_id with error.type, error.code, and the HTTP status. Logging it on success too means you can correlate a later report with the originating call.