# Errors

Every error response uses a single, predictable envelope — the same shape across
the entire API. Branch your handling on the machine-readable `type` and `code`;
show or log the `message`; quote the `request_id` to support.

## The error envelope

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_missing",
    "message": "tier is required.",
    "param": "tier"
  }
}
```

| Field | Always present | Description |
| --- | --- | --- |
| `type` | yes | High-level category — branch your error handling on this. |
| `code` | yes | Specific machine-readable code within the type. |
| `message` | yes | Human-readable description. Safe to log; don't parse it. |
| `param` | no | The offending request parameter, when one applies. Omitted otherwise. |
| `request_id` | no | Correlation id — present on authentication, authorization, rate-limit, and unexpected platform errors. Every response (success or error) also carries it in the `x-request-id` header. |

## Error types

| `type` | Meaning | Typical HTTP status |
| --- | --- | --- |
| `invalid_request_error` | The request was malformed, missing a field, or violated a business rule (e.g. sold-out tier, unverified payment). | `400`, `402`, `404`, `409` |
| `authentication_error` | The API key is missing, invalid, revoked, or of the wrong kind for the endpoint. | `401` |
| `authorization_error` | The key is valid but not allowed to do this — usually a missing scope. | `403` |
| `idempotency_error` | An `Idempotency-Key` was reused with a different request. | `409` |
| `queue_error` | The customer has not (yet) been admitted by the event's on-sale waiting room. | `403` |
| `rate_limit_error` | You've made too many requests too quickly. | `429` |
| `api_error` | An unexpected error on the platform side. Safe to retry. | `500`, `502` |

## Common codes

| `code` | `type` | When you'll see it |
| --- | --- | --- |
| `unauthorized` | `authentication_error` | The key is unknown, revoked, malformed, or the wrong kind for the endpoint. |
| `forbidden` | `authorization_error` | The key is missing the scope this operation requires. |
| `idempotency_key_reuse` | `idempotency_error` | Same `Idempotency-Key`, different request body. |
| `parameter_missing` / `parameter_invalid` | `invalid_request_error` | A required field is absent or a field has a bad value — `param` names it. |
| `resource_missing` | `invalid_request_error` | The resource doesn't exist, or isn't yours. |
| `sold_out` | `invalid_request_error` | Issuing from a tier with no remaining supply. |
| `payment_required` | `invalid_request_error` | A paid ticket or upgrade was requested without a `payment_intent_id`. |
| `not_admitted` | `queue_error` | The customer must be admitted by the waiting room before issuance. |

> **Note:** New `code` values can be added over time within an existing `type`. Always handle
> the `type` you recognize and treat unknown `code`s within it gracefully rather
> than failing hard.


## HTTP status mapping

The HTTP status and the `error.type` agree, so you can react to either:

| Status | Meaning |
| --- | --- |
| `400` | Invalid request — fix the payload or the business condition. |
| `401` | Authentication failed — check your key, its kind, and mode. |
| `402` | Payment required — a paid ticket or upgrade lacks a verified payment. |
| `403` | Not allowed — missing scope, a not-yet-released tier, or a customer not admitted by the waiting room. |
| `404` | The resource doesn't exist, or isn't yours. |
| `409` | Conflict — idempotency-key reuse, sold-out supply, or a payment already used. |
| `429` | Rate limited — back off and retry. |
| `500` / `502` | Platform or payment-provider error — safe to retry, ideally with backoff. |

## Using `request_id` for support

Every response — success or error — carries a correlation id in the
`x-request-id` response header; authentication, authorization, rate-limit, and
unexpected platform errors also include it in the envelope as
`error.request_id`. When something goes wrong, **capture and log it**. If you
contact support, quoting the id lets us locate the exact request instantly —
no guessing required.

```bash
# Log the correlation id from any response.
curl -si https://api.ticketconnect.example/v1/events/evt_does_not_exist \
  -H "Authorization: Bearer sk_test_your_key_here" \
  | grep -i x-request-id
```

## Example error responses

**401 Authentication**

```json
{
  "error": {
    "type": "authentication_error",
    "code": "unauthorized",
    "message": "Invalid API key",
    "request_id": "1b9d33a0-…"
  }
}
```

**409 Idempotency**

```json
{
  "error": {
    "type": "idempotency_error",
    "code": "idempotency_key_reuse",
    "message": "This Idempotency-Key was used with a different request."
  }
}
```

**429 Rate limit**

```json
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limited",
    "message": "Rate limit exceeded",
    "request_id": "4e8a90fb-…"
  }
}
```

See [Idempotency](#idempotency) for the `409` case in context.
