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

# Errors

> Stripe-shaped error envelope. Every failure looks the same.

Every error response — validation, auth, rate limit, not found, server — uses the same JSON shape:

```json theme={null}
{
  "error": {
    "type": "invalid_request",
    "code": "attribute_enum_invalid",
    "message": "\"vip\" is not a valid value for \"lifecycle_stage\"; expected one of: lead, customer, churned",
    "param": "lifecycle_stage"
  }
}
```

## `type`

Coarse category. One of:

| `type`            | When                                                                                                                                                 |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_request` | Bad input — missing field, wrong type, bad enum, etc. (mostly 400)                                                                                   |
| `authentication`  | API key missing, invalid, or revoked (401)                                                                                                           |
| `permission`      | Authenticated but not allowed — e.g. a read-only credential (API key or OAuth token) attempting a write returns `403` with code `insufficient_scope` |
| `rate_limit`      | Bucket exhausted; check `Retry-After` (429)                                                                                                          |
| `idempotency`     | `Idempotency-Key` mismatch or in-flight conflict (409 / 422)                                                                                         |
| `not_found`       | Resource doesn't exist or you can't see it (404)                                                                                                     |
| `conflict`        | Slug already used, unique violation, etc. (409)                                                                                                      |
| `server`          | Internal error (500)                                                                                                                                 |

## `code`

Specific machine-readable identifier. Stable per `(type, code)` pair — safe to switch on:

```ts theme={null}
if (error?.code === 'attribute_enum_invalid') {
  // surface the message to the user; check error.param
}
```

## `param`

If the error is about a specific field, `param` names it (e.g., `email`, `custom_attributes.tier`). Absent for non-field errors.

## `message`

Human-readable string. Do not parse this — `code` is the stable contract.
