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

# Errors

> Error response format and common error codes.

When an API endpoint request fails, the API returns a JSON error response with a consistent structure.

## Error format

API endpoint errors follow this envelope:

```json theme={null}
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid",
    "details": [
      {
        "field": "email",
        "message": "is required"
      }
    ]
  }
}
```

| Field     | Description                                                              |
| --------- | ------------------------------------------------------------------------ |
| `code`    | A machine-readable error code (see table below)                          |
| `message` | A human-readable description of what went wrong                          |
| `details` | An optional array of field-level errors (present on validation failures) |

## Error codes

This table also includes OAuth token endpoint errors. Those happen before you have a bearer token and are returned by `/oauth/token`.

| Code                 | HTTP Status | Description                                                                                                                                                                      |
| -------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_client`     | 401         | Client ID or client secret is incorrect. Returned by the `/oauth/token` endpoint.                                                                                                |
| `invalid_scope`      | 401         | The requested OAuth scope is not enabled on the application. Returned by the `/oauth/token` endpoint.                                                                            |
| `unauthorized`       | 401         | Bearer token is missing, expired, revoked, or invalid. OAuth access tokens expire after 2 hours. API token lifetimes depend on the expiration chosen when the token was created. |
| `insufficient_scope` | 403         | Token does not have the required scope for this endpoint. See [Scopes](/docs/api-reference/authentication#scopes).                                                               |
| `not_found`          | 404         | The contact could not be found                                                                                                                                                   |
| `conflict`           | 409         | A contact with this email or phone number already exists                                                                                                                         |
| `validation_failed`  | 422         | One or more fields failed validation                                                                                                                                             |
| `payload_too_large`  | 413         | The request body exceeds the endpoint's size limit (256 KB for [Ingest an event](/docs/api-reference/ingest-event))                                                              |
| `rate_limited`       | 429         | Rate limit exceeded — see [Rate Limits](/docs/api-reference/rate-limits)                                                                                                         |

## Validation errors

A `422 validation_failed` response includes a `details` array with specific field errors. For example, creating a contact without an email or phone number returns:

```json theme={null}
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid",
    "details": [
      {
        "field": "base",
        "message": "email or phone_number is required"
      }
    ]
  }
}
```

Common validation messages:

| Field                    | Message                                                                     |
| ------------------------ | --------------------------------------------------------------------------- |
| `email` / `phone_number` | `email or phone_number is required`                                         |
| `phone_number`           | `must be a valid phone number in E.164 format`                              |
| `email_consent`          | `must be one of: subscribed, unsubscribed, never_subscribed, suppressed`    |
| `sms_consent`            | `must be one of: subscribed, unsubscribed, never_subscribed, single_opt_in` |
| `sms_consent`            | `requires a phone number from a supported country`                          |
| `custom_fields`          | `must be a flat key-value object (no nested values)`                        |

## Conflict errors

A `409 conflict` is returned when you try to create a contact with an email or phone number that already belongs to an existing contact. The response includes the `id` of the existing contact so you can update it directly.

```json theme={null}
{
  "error": {
    "code": "conflict",
    "message": "A contact with this email already exists"
  },
  "id": "cus_x9y8z7w6v5u4t3s2"
}
```

To update the existing contact, use the [Update a contact](/docs/api-reference/update-contact) endpoint with the returned `id`.

## Event ingest errors

The [Ingest an event](/docs/api-reference/ingest-event) endpoint is an exception: a
payload that fails validation returns `400` with a **top-level `errors` array**
rather than the standard `error` envelope, and a `status` of `rejected`.

```json theme={null}
{
  "event_id": "507f1f77bcf86cd799439012",
  "status": "rejected",
  "errors": [
    { "field": "identifier", "message": "email, phone, or privy_id is required" },
    { "field": "event_type", "message": "is required" }
  ]
}
```

Auth, scope, size, and rate-limit failures on that endpoint (`401`, `403`, `413`,
`429`) still use the standard `error` envelope above.

## Handling errors

1. **Check the HTTP status code first.** The status code tells you the category of error.
2. **Parse the error body.** Use the `code` field for programmatic handling and the `message` for logging.
3. **Only retry on `429`.** Rate limit errors are temporary — wait for `Retry-After` seconds, then retry. Other errors require fixing the request.
