Skip to main content
Every native /v1 (+ Data API) error shares a single envelope shape. The HTTP status code and error.code are the two identifiers to branch on — error.message is human-readable and may change without notice. The /v1/messages and /v1/chat/completions compat endpoints are the exception: they intentionally return Anthropic’s and OpenAI’s own wire shapes instead (more detail in the closed-set paragraph after the example) — on those, the HASP code is carried as error.hasp_code, and error.code/error.type are the compat SDK’s own fields, not this envelope’s.

Error envelope

request_id appears in both error and meta — the duplication is intentional for convenience when parsing either field. It also appears on the Request-Id response header, including on SSE streams. On this native envelope, error.type is a closed set: invalid_request, authentication, permission, not_found, conflict, rate_limited, payment_required, api_error. Branch on error.code for anything more specific — error.type is the coarse bucket. This closed set is specific to the native envelope — the /v1/messages and /v1/chat/completions compat endpoints intentionally return Anthropic’s and OpenAI’s own wire shapes (e.g. invalid_request_error, not_found_error) instead, so an existing Anthropic or OpenAI SDK keeps working unmodified against them.

Error codes by HTTP status

Codes marked (Agent SDK) are only ever returned to delegated agent-credential (hasp_agt_*) callers — see Agents overview. Every code below except the ones in the “Legacy AI-surface codes” callouts is a case on the closed App\Support\Api\ErrorCode enum and is covered by an automated CI test that fails the build if this table drifts from the enum (tests/Feature/Api/V1/ErrorCatalogConformanceTest.php).

400 — invalid_request

401 — authentication

402 — payment_required

403 — permission

404 — not_found

405 — invalid_request

409 — conflict

410 — gone

422 — invalid_request

429 — rate_limited

502 — provider_error

503 — service_unavailable

500 — api_error

Idempotency

Send an Idempotency-Key header on a JSON POST or mutating PATCH/PUT request to make retries safe. Replaying the same key with the same request body within 24 hours returns the original response with an Idempotent-Replayed: true header instead of re-executing the request. Reusing a key with a different request body is rejected as a 409 IDEMPOTENCY_CONFLICT. The key is optional — omitting it disables idempotency for that request. Idempotency has two boundaries. It only applies to JSON request bodies — a multipart or form-encoded request (e.g. file uploads) is never deduped, regardless of the header. And the AI inference endpoints (/v1/messages, /v1/messages/count_tokens, /v1/chat/completions, /v1/ai/chat) are excluded entirely, since their response content may carry re-identified PHI that this primitive must not persist — an Idempotency-Key sent to those endpoints is silently ignored. Full treatment (retry semantics, key generation guidance, worked examples): Idempotency.

Versioning

Every successful response is versioned under a dated Hasp-Version (YYYY-MM-DD), independent of the /v1 URL. Send a Hasp-Version header to pin a request to a specific version; omit it to use your API key’s configured default, or the deployment default if the key has none. The resolved version is echoed back on the Hasp-Version response header for any request that reaches version resolution — errors raised before that point (e.g. 404 for an unrecognized route, 405 for an unsupported method) do not carry the header. A version, once published, never changes shape underneath a pinned caller. Full treatment (per-key pinning, the /v1 major boundary, deprecation-header policy): Versioning.

Streaming errors

On SSE streams, errors after the stream has opened are delivered as events rather than HTTP status codes:
Streaming failures emit a standalone error event and the stream ends — there is no run.failed event and no failure envelope. See Streaming events for the payload shape.

Handling retryable errors

Complete code list

Every error code the API can return, with its HTTP status. This table is generated from the platform’s ErrorCode enum, so it is exhaustive by construction — the curated sections above explain the ones you are most likely to handle.