---
title: "API error reference - envelope and status codes"
description: "The HyperCRM API error reference: the success and error envelope shapes, request-id correlation, and every error code with its own HTTP status."
canonical: "https://hypercrm.app/docs/api/errors"
lang: "en"
updated: "2026-09-14"
---

_API reference / Errors_

# Every error code, in one place

Every HyperCRM API response, success or failure, follows one of two envelope shapes. This page lists both shapes, how request ids work, and the full table of error codes and HTTP statuses an endpoint can return.

[Start the free trial](/dashboard) · [Back to API reference](/docs/api)

## The two envelopes

Successful responses (2xx) always look like this, with `data` holding the resource and `requestId` for correlation:

```json
{
  "data": { "...": "resource-specific shape" },
  "requestId": "req_5f2c1e40-..."
}
```

`POST` endpoints that create a resource return 201 with the same shape. Error responses (4xx/5xx) share the mirror shape, with a stable `code`, a safe-to-display `message`, the same `requestId`, and an optional `details` array present only when there are field-level issues (e.g. field validation entries with `path`/`message`):

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Request validation failed.",
    "requestId": "req_5f2c1e40-...",
    "details": [ { "path": ["firstName"], "message": "firstName is required" } ]
  }
}
```

## Request ids

Every response carries an `x-request-id` header, and the same value appears as `requestId` in the body. If the incoming request supplies its own `x-request-id` (or `x-correlation-id`) header matching a safe pattern, that value is echoed back for client-side log correlation; otherwise the server generates one of the form `req_<uuid>`.

Back to the [API reference hub](/docs/api) for the other resource pages.

## Every error code

Statuses are fixed per code. Some endpoints override the message with something more specific while keeping the same code and status, so match on the code — never on the message text.

| Code | Status | When it happens |
| --- | --- | --- |
| validation_failed | 400 | A field failed validation, the body carried an unrecognized field, or the JSON was malformed. Carries details. |
| bad_request | 400 | A malformed request that is not a field-level validation failure. |
| unauthorized | 401 | Authentication is required. Distinct from the bearer-token codes below, which describe the token itself. |
| forbidden | 403 | Authenticated, but not allowed to do this — for example exporting data as a non-owner. |
| not_found | 404 | No such resource in your practice. Also returned for a malformed path id, so the two are indistinguishable. |
| conflict | 409 | The request conflicts with current state — a duplicate tag title, for instance. |
| already_submitted | 409 | A public form link that has already been submitted. |
| entries_not_billable | 409 | Issuing a receipt or invoice for chosen charges when one of them is not an unbilled charge of that patient. |
| appointment_already_charged | 409 | Charging an appointment that already has a charge that is not voided. Void that charge first. |
| appointment_not_chargeable | 409 | Charging an appointment that is not a confirmed patient appointment: a request, a declined, cancelled or expired booking, a reminder or a blocked slot. |
| token_expired | 410 | A public link has expired. |
| payload_too_large | 413 | The request body exceeds the maximum accepted size. |
| recipient_tax_id_required | 422 | A tax invoice was requested for a patient with no tax id on file. Add it, or issue a receipt. |
| rate_limit_exceeded | 429 | A rate limit was reached. |
| internal_error | 500 | An unexpected server error. Nothing about the cause is returned; quote the requestId when reporting it. |
| service_unavailable | 503 | A dependency is temporarily unavailable. |
| missing_authorization | 401 | No Authorization header was sent. |
| invalid_authorization_header | 401 | The header was present but not in the form Bearer <token>. |
| invalid_auth_token | 401 | The token is not valid. |
| expired_auth_token | 401 | The token has expired. Refresh it and retry. |
| revoked_auth_token | 401 | The token has been revoked. |
| auth_token_verification_failed | 401 | The token could not be verified for a reason with no more specific code. |
| disabled_auth_token | 403 | The token verifies, but the identity behind it is disabled — hence 403 rather than 401. |
| unsupported_auth_provider | 403 | That sign-in provider is not accepted for API access. |
| auth_provider_email_required | 403 | The sign-in provider did not supply an email address, which is required to create an account. |
| user_disabled | 403 | The HyperCRM account itself is disabled. |
| auth_configuration_missing | 503 | Authentication is not configured on the server. |

## Common questions

### How is patient data protected?

Patient records are encrypted in transit and at rest, files are stored privately and served through short-lived signed links, and every change is written to an audit log. Staff accounts sign in with passkeys rather than shared passwords.

### Is `details` always present on an error response?

No. It's included only when there are structured, field-level issues to report, such as field validation failures, each with a path and a message. When there's nothing field-specific to say, the key is omitted entirely from the response body rather than sent as an empty array.

### How do I correlate a failed request with server-side logs?

Use the requestId value from the response body, or the identical x-request-id response header. If your own request already sent an x-request-id or x-correlation-id header in the expected format, that same value is echoed back, so you can generate your own id up front and match on it end to end.

### Why is a disabled account a 403 instead of a 401?

Because the bearer token itself is still valid and verifies correctly — the identity behind it is disabled, which HyperCRM treats as an authorization failure rather than an authentication failure. It's a deliberate exception from the more common convention, documented explicitly on the authentication reference page.