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

# Errors

> The error shape, every error type, and what to do about each one.

Every error has the same shape:

```json theme={null}
{
  "error": {
    "type": "invalid_attributes",
    "message": "Some attributes could not be stored.",
    "details": [{ "key": "exam_year", "message": "expected a number" }]
  }
}
```

`type` is a stable machine name. Branch on it. `message` is one sentence for a
human reader, and it can change. `details` is present when Kai can name the
attributes or the identities that caused the error.

## Status codes

| Code | Meaning                                                |
| ---- | ------------------------------------------------------ |
| 200  | The request succeeded.                                 |
| 201  | A record was created.                                  |
| 400  | Kai could not read the request.                        |
| 401  | The API key is missing or not valid.                   |
| 403  | The key lacks the permission that this endpoint needs. |
| 404  | There is no such record in this workspace.             |
| 409  | The write conflicts with a record that exists.         |
| 422  | Kai read the request but could not store an attribute. |

The batch endpoint returns 200 whenever Kai understands the request, even when
every operation in it failed. Read `summary.errors` and the `results` array.

## Error types

### `unauthorized` (401)

The key is missing, unknown, revoked or expired. The message is the same for
all four, so a caller cannot learn which key ids exist.

Make sure that the header is `Authorization: Bearer kai_live_...`. Then make
sure that the key is not revoked, in **Settings → API**.

### `forbidden` (403)

The key is valid but lacks a permission. The message names the permission.
Create a key with that permission, in **Settings → API**.

### `invalid_request` (400)

The body or a query parameter is wrong. The message says which one. Common
causes:

* the request carries no identity, so Kai has nothing to match on;
* a field holds the wrong JSON type;
* an `owner` value that no member of the workspace holds;
* a `cursor` value that Kai did not issue.

### `identity_conflict` (409)

The identities in the request belong to two different contacts. Kai does not
merge contacts. `details` names the identities.

Correct the conflict in Kai, under **Contacts**, or write to each contact by
its Kai id. See [Identity](/concepts/identity).

### `invalid_attributes` (422)

One or more attributes could not be stored. `details` names each key and the
reason:

| Reason                                 | What to do                                               |
| -------------------------------------- | -------------------------------------------------------- |
| `no such workspace attribute`          | Create the attribute with `POST /attributes`.            |
| `this workspace attribute is archived` | Restore it with `DELETE /attributes/{key}?restore=true`. |
| `expected a number`                    | Send a number, or a string that holds one.               |
| `expected one of: ...`                 | Send one of the options of the `select` attribute.       |
| `expected a date as YYYY-MM-DD`        | Send a calendar day, not a timestamp.                    |

Kai stores none of the attributes in a request that holds a bad one. Correct
the value and send the request again.

### `type_conflict` (409)

`POST /attributes` names a key that exists and stores a different type. The
type of an attribute cannot change. Archive the attribute and create another
one with a new API name.

### `too_many_operations` (400)

A batch holds more than 1000 operations. The message says how many requests to
send. See [Import many contacts](/guides/bulk-import).

### `not_found` (404)

There is no record with this id in the workspace of the key. A key reaches one
workspace only, so an id from another workspace answers 404 and not 403.

## Retries

A `500` response and a network timeout can be retried. All write endpoints
match on identity, so a repeated request updates the same contact and does not
make a copy.

Do not retry a 400, a 403, a 409 or a 422. The same request fails the same
way. Correct the request first.
