# Errors (/docs/errors)



Eventlayer uses standard HTTP status codes and a stable error code for each kind of failure. Every error code always has the same HTTP status and title.

## Error schema [#error-schema]

Errors use an `error` object inside the standard API response envelope:

```json
{
  "status": 422,
  "error": {
    "type": "https://eventlayer.dev/docs/errors#validation-failed",
    "title": "Request validation failed",
    "status": 422,
    "code": "VALIDATION_FAILED",
    "detail": "One or more fields are invalid.",
    "requestId": "req_01k2example",
    "fields": [
      {
        "field": "endAt",
        "message": "The end time must be after the start time."
      }
    ]
  }
}
```

| Field       | Type   | Description                                                                |
| ----------- | ------ | -------------------------------------------------------------------------- |
| `type`      | string | Stable URI identifying the problem category.                               |
| `title`     | string | Stable summary associated with the error code.                             |
| `status`    | number | HTTP status associated with the error code.                                |
| `code`      | string | Stable value for programmatic error handling.                              |
| `detail`    | string | Explanation specific to this occurrence.                                   |
| `requestId` | string | Identifier to include when contacting support.                             |
| `fields`    | array  | Fields related to the failure. Empty when the error is not field-specific. |

<Callout type="info">
  Branch on `code`, `status`, and `fields[].field`. Treat `title`, `detail`, and
  `fields[].message` as human-readable text that may improve over time.
</Callout>

## Handling errors with TypeScript [#handling-errors-with-typescript]

The TypeScript SDK returns API failures as data rather than throwing them:

```ts title="errors.ts · server only"
const {
  data: ticket,
  error,
  response,
} = await eventlayer.tickets.checkIn({ ticketId: "tkt_123" });

if (error) {
  switch (error.code) {
    case "TICKET_ALREADY_CHECKED_IN":
      // This operation was already completed.
      break;
    case "VALIDATION_FAILED":
      for (const field of error.fields) {
        console.error(field.field, field.message);
      }
      break;
    case "RATE_LIMIT_EXCEEDED":
      console.error(response.headers.get("retry-after"));
      break;
    default:
      console.error(error.requestId, error.detail);
  }
} else {
  console.log(ticket.status);
}
```

Network failures where no HTTP response is received still throw.

## Error reference [#error-reference]

### `INVALID_REQUEST` [#invalid_request]

* **Status:** `400 Bad Request`
* **Message:** The request could not be parsed or understood.
* **Suggested action:** Check the request syntax, JSON body, and query-string encoding before retrying.

### `AUTHENTICATION_REQUIRED` [#authentication_required]

* **Status:** `401 Unauthorized`
* **Message:** The operation requires authentication using one of its supported authentication schemes.
* **Suggested action:** Supply your API key in the Authorization header. See [authentication](/docs/authentication).

### `INVALID_API_KEY` [#invalid_api_key]

* **Status:** `401 Unauthorized`
* **Message:** The supplied API key is invalid, expired, or revoked.
* **Suggested action:** Check the `Authorization: Bearer` header or create a replacement key. See [Authentication](/docs/authentication).

### `PASS_TOKEN_INVALID` [#pass_token_invalid]

* **Status:** `401 Unauthorized`
* **Message:** The supplied Apple Wallet pass token is invalid.
* **Suggested action:** Generate a new signed pass URL instead of modifying or reusing the invalid token.

### `PASS_TOKEN_EXPIRED` [#pass_token_expired]

* **Status:** `401 Unauthorized`
* **Message:** The supplied Apple Wallet pass token has expired.
* **Suggested action:** Generate a new signed pass URL and retry the download.

### `NOT_FOUND` [#not_found]

* **Status:** `404 Not Found`
* **Message:** The directly addressed resource could not be found.
* **Suggested action:** Verify the resource ID and account scope. Eventlayer may return the same error when a resource exists outside the authenticated account.

### `ROUTE_NOT_FOUND` [#route_not_found]

* **Status:** `404 Not Found`
* **Message:** The requested API route does not exist.
* **Suggested action:** Check the base URL, API path, and SDK version.

### `METHOD_NOT_ALLOWED` [#method_not_allowed]

* **Status:** `405 Method Not Allowed`
* **Message:** The HTTP method is not supported by the requested route.
* **Suggested action:** Use the method documented in the API reference or call the corresponding SDK resource method.

### `FAILED_PRECONDITION` [#failed_precondition]

* **Status:** `409 Conflict`
* **Message:** The target resource does not satisfy a prerequisite for the operation.
* **Suggested action:** Read `detail` and `fields`, update the resource state or configuration, and retry. For example, an event must have a ticket type before tickets can be issued.

### `TICKET_ALREADY_CHECKED_IN` [#ticket_already_checked_in]

* **Status:** `409 Conflict`
* **Message:** The ticket has already been checked in.
* **Suggested action:** Treat the check-in as already completed or show the existing check-in state. Do not repeatedly retry the mutation.

### `UNSUPPORTED_MEDIA_TYPE` [#unsupported_media_type]

* **Status:** `415 Unsupported Media Type`
* **Message:** The request uses a content type that the endpoint does not accept.
* **Suggested action:** Use the `Content-Type` documented for the endpoint. JSON requests normally use `application/json`; image uploads use multipart form data.

### `VALIDATION_FAILED` [#validation_failed]

* **Status:** `422 Unprocessable Content`
* **Message:** One or more submitted fields or references are invalid for the operation.
* **Suggested action:** Use the `fields` array to associate each message with the corresponding request field, correct the values, and retry.

### `PAYLOAD_TOO_LARGE` [#payload_too_large]

* **Status:** `413 Content Too Large`
* **Message:** The request or uploaded image exceeds the permitted size.
* **Suggested action:** Reduce or compress the payload before retrying.

### `RATE_LIMIT_EXCEEDED` [#rate_limit_exceeded]

* **Status:** `429 Too Many Requests`
* **Message:** The API key has exceeded its request rate.
* **Suggested action:** Respect the `Retry-After` response header and reduce concurrency. See [Rate Limits](/docs/rate-limits).

### `REQUEST_TIMEOUT` [#request_timeout]

* **Status:** `408 Request Timeout`
* **Message:** Eventlayer could not complete the request within the permitted time.
* **Suggested action:** A timeout can leave the outcome unknown. See [retries and recovery](/docs/retries) before repeating a write.

### `INTERNAL_SERVER_ERROR` [#internal_server_error]

* **Status:** `500 Internal Server Error`
* **Message:** Eventlayer could not complete the request because of an unexpected server failure.
* **Suggested action:** Retry with backoff. If the problem continues, contact support and include `requestId`.

## Status code summary [#status-code-summary]

| Status | Codes                                                                                    |
| ------ | ---------------------------------------------------------------------------------------- |
| `400`  | `INVALID_REQUEST`                                                                        |
| `401`  | `AUTHENTICATION_REQUIRED`, `INVALID_API_KEY`, `PASS_TOKEN_INVALID`, `PASS_TOKEN_EXPIRED` |
| `404`  | `NOT_FOUND`, `ROUTE_NOT_FOUND`                                                           |
| `405`  | `METHOD_NOT_ALLOWED`                                                                     |
| `408`  | `REQUEST_TIMEOUT`                                                                        |
| `409`  | `FAILED_PRECONDITION`, `TICKET_ALREADY_CHECKED_IN`                                       |
| `413`  | `PAYLOAD_TOO_LARGE`                                                                      |
| `415`  | `UNSUPPORTED_MEDIA_TYPE`                                                                 |
| `422`  | `VALIDATION_FAILED`                                                                      |
| `429`  | `RATE_LIMIT_EXCEEDED`                                                                    |
| `500`  | `INTERNAL_SERVER_ERROR`                                                                  |



## Next steps [#next-steps]

<Cards>
  <Card icon="<KeyRound />" title="Authentication" href="/docs/authentication" description="Configure bearer authentication for API requests." />

  <Card icon="<Gauge />" title="Rate Limits" href="/docs/rate-limits" description="Handle rate limiting and retries safely." />
</Cards>
