Errors
Every failed request returns the same envelope, regardless of which endpoint produced it. Branch on the HTTP status for broad categories, on code for specific failures you want to react to programmatically, and surface error in your UI.
Error envelope
{
"error": "Email is already in use by another user in this organization.",
"code": "duplicate_email",
"request_id": "req_9f7c9d3a-2b1e-4c5f-bd91-7e2c4a8d6f10",
"details": { "field": "email" }
}| Field | Description |
|---|---|
error | Human-readable message. Safe to surface in a UI. Wording may change between releases — do not branch on it. |
code | Stable machine-readable identifier (snake_case). Branch on this when you need to react to a specific error programmatically. See the catalog below. |
request_id | Unique identifier for the request. Include in support tickets and bug reports. Also returned in the X-Request-Id response header. |
details | Optional structured context (e.g. the offending field name for a validation error). Shape varies by code; do not assume any particular keys are present. |
HTTP status codes
Cortex uses the standard HTTP status code set. The status code is the primary signal — clients should switch on it first.
| Status | Category | When |
|---|---|---|
400 | Bad request | The request body is malformed, required fields are missing, or a query parameter has the wrong type. |
401 | Unauthenticated | Missing, invalid, or expired token. Re-authenticate before retrying. |
403 | Forbidden | Authenticated, but the identity is not permitted to perform this action on this resource. |
404 | Not found | The resource does not exist, or it exists but is not visible to your organization. The API does not distinguish these cases. |
409 | Conflict | The request conflicts with the current state of a resource — for example, a uniqueness constraint or a state-machine violation. |
422 | Unprocessable | The request is well-formed but the data fails semantic validation (e.g. an email is not a valid email). |
429 | Rate limited | Too many requests in too short a window. Back off and retry. The Retry-After header, when present, indicates a minimum wait in seconds. |
500 | Server error | An unexpected error on the server. Safe to retry with exponential backoff. |
Common error codes
The code field is stable: once a code ships, its meaning does not change. New codes may be added over time. Treat any code your client does not recognize as a generic failure of the same HTTP status category.
| Code | Status | Meaning |
|---|---|---|
invalid_request | 400 | The request body or query parameters failed schema validation. |
missing_field | 400 | A required field is absent. details.field names it. |
invalid_token | 401 | The bearer token is missing, malformed, or could not be verified. |
token_expired | 401 | The token was valid but its expiry time has passed. |
invalid_credentials | 401 | Wrong email/password on session mint, or a genuine token/API key belonging to a suspended or expired organization on mint or renew. |
forbidden | 403 | The caller is authenticated but not authorized for this action. |
org_suspended | 403 | The caller's organization is suspended or, for a demo organization, past its expiry. Every member route is blocked uniformly; extend or unsuspend the organization to restore access. Session mint and renew reject a blocked organization earlier, with a generic 401 invalid_credentials instead — so those two endpoints never leak the organization's suspension state. |
not_found | 404 | The resource does not exist or is not visible to the caller's organization. |
duplicate_email | 409 | The email address is already in use by another user in this organization. |
conflict | 409 | Generic state conflict — typically a uniqueness or state-machine violation. |
managed_externally | 409 | The resource is owned by an external system (e.g. a Stripe-source subscription) and cannot be mutated through Cortex. |
validation_failed | 422 | The request is well-formed but failed business-rule validation. |
rate_limited | 429 | Too many requests in too short a window. |
internal_error | 500 | An unexpected server-side error. Retry with backoff; include the request_id if reporting. |
Request IDs
Every response — successful or not — carries a unique request identifier in the X-Request-Id header. Error responses also include the same value in the request_id field of the response body. When something goes wrong, capture the request ID and include it in any support request. It lets us correlate your call with our server-side logs in a single lookup.
Errors on streams
On the streaming surface, authentication and authorization errors are returned as a normal HTTP 4xx response before the stream opens. Errors that occur after the stream is open are surfaced as a lifecycle.failed event whose data payload carries the same envelope documented above.