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

# Errors and idempotency

> Handle validation, authorization, conflicts, rate limits, and safe write retries.

REST errors use one JSON envelope:

```json theme={"system"}
{
  "error": {
    "code": "TENANT_SCOPE_MISSING",
    "message": "Missing required scope crm:write"
  }
}
```

MCP tools return the same envelope as structured content with `isError: true`.
Clients should branch on `error.code`; messages are intended for people and may
be refined over time.

## HTTP status behavior

| Status | Meaning                                                                             |
| ------ | ----------------------------------------------------------------------------------- |
| `401`  | The bearer token is missing, invalid, expired, or for another resource              |
| `403`  | The token lacks a scope, the user lacks permission, or the request origin is denied |
| `404`  | The resource does not exist within the authorized tenant boundary                   |
| `409`  | A unique field conflicts or an idempotent operation conflicts or is still running   |
| `422`  | The request shape or write input is invalid                                         |
| `429`  | The tenant API rate limit or abuse protection denied the request                    |
| `500`  | An unexpected server error occurred                                                 |

A tenant-scoped `404` deliberately does not reveal whether an ID exists in
another organization.

On `401`, inspect the `WWW-Authenticate` header. It includes the protected-
resource metadata URL clients can use to restart OAuth discovery.

## Idempotency keys

Send `Idempotency-Key` on REST creates, updates, and deletes. MCP write tools
accept the equivalent `idempotencyKey` input.

```http theme={"system"}
Idempotency-Key: crm-import-2026-07-10-row-0042
```

Keys must be non-empty and no longer than 200 characters. Scope a key to one
logical user action, and reuse it only when all of these are unchanged:

* organization
* OAuth client
* interface source
* operation and route identifiers
* normalized request input

When the first call succeeds, a retry with the same key and request replays the
stored response. A create replay returns the original resource rather than
creating another one.

When the same key is reused with different input, Silo returns
`TENANT_IDEMPOTENCY_KEY_CONFLICT`. When the original operation is still being
processed, Silo returns `TENANT_IDEMPOTENCY_KEY_IN_PROGRESS`. Back off and
retry the exact same request with the same key.

## Retry rules

| Result                           | Client action                                                  |
| -------------------------------- | -------------------------------------------------------------- |
| Network timeout with no response | Retry the exact request with the same idempotency key          |
| `409` in progress                | Back off, then retry the exact request with the same key       |
| `409` key conflict               | Stop and generate a new key only for a genuinely new action    |
| `422`                            | Correct the request; do not retry unchanged input              |
| `429`                            | Honor `Retry-After` when present and apply exponential backoff |
| `500`                            | Retry cautiously with the same idempotency key                 |

Do not automatically retry permission failures or tenant-scoped not-found
responses. Reauthorize or correct the selected organization and resource IDs.
