> ## Documentation Index
> Fetch the complete documentation index at: https://docs.humanos.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> HTTP status codes the Humanos API returns, the error response shapes, and how to handle them

The Humanos API uses conventional HTTP status codes: `2xx` for success, `4xx` for problems with the request, and `5xx` for problems on our side. Error responses are JSON.

## Status codes

| Code          | Meaning           | When you'll see it                                                                                                                                                                      |
| ------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` / `201` | Success           | The request completed; `201` on resource creation.                                                                                                                                      |
| `400`         | Bad Request       | Validation failed — a field is missing, has the wrong type, or an unknown field was sent (unknown fields are rejected, not ignored). See the validation shape below.                    |
| `401`         | Unauthorized      | Missing or invalid API key, or the request signature didn't verify. Check both the `Authorization` header and the signature headers — see [Authentication](/essentials/authentication). |
| `403`         | Forbidden         | The API key is valid but not allowed to perform this operation on this resource.                                                                                                        |
| `404`         | Not Found         | The resource doesn't exist **or doesn't belong to your organization** — the API deliberately doesn't distinguish the two.                                                               |
| `429`         | Too Many Requests | You hit a rate limit. Back off and retry — see [Rate limits](/essentials/rate-limits).                                                                                                  |
| `5xx`         | Server error      | Something failed on our side. Safe to retry idempotent requests (GETs) with backoff.                                                                                                    |

## Validation errors (400)

Validation failures return a `message` array with one entry per failing field. Each entry names the field, echoes the offending value, and lists the violated constraints:

```json theme={"dark"}
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": [
    {
      "field": "phoneNumber",
      "value": "not-a-number",
      "constraints": {
        "isPhoneNumber": "phoneNumber must be a valid phone number"
      },
      "children": []
    }
  ]
}
```

Two behaviours worth knowing:

* **Unknown fields are rejected.** The API whitelists the documented fields for each endpoint; sending anything else fails the request rather than being silently dropped. This catches typos (`phonenumber` vs `phoneNumber`) at the door.
* **Nested objects report through `children`.** When a nested field fails, the parent entry's `children` array carries the nested validation errors — each naming the inner field under a `property` key, with its own `constraints` (and deeper `children` if the nesting continues).

## Authentication errors (401)

`401` responses carry a short message identifying which layer failed:

```json theme={"dark"}
{
  "statusCode": 401,
  "message": "Invalid signature",
  "error": "Unauthorized"
}
```

Work through the layers in order: is the `Authorization: Bearer <api-key>` header present and correct → is the signature computed over the exact raw body with the right secret → is the timestamp fresh. The [SDK](/essentials/sdk) signs every request automatically, which eliminates the most common cause.

## Handling guidance

* **Don't retry `400`/`401`/`403`** — the request will fail the same way until you change it.
* **Treat `404` as "not visible to me"**, not proof of non-existence.
* **Retry `429` after backing off** — the window is one minute, so a short exponential backoff clears it. See [Rate limits](/essentials/rate-limits).
* **Retry `5xx` GETs with backoff.** For mutating calls, check state first (e.g. `GET /request/{id}`) before re-sending, so you don't create duplicates.
