export const metadata = {
  title: "Errors",
  description:
    "Stable error shape and every error type the FaithTranscripts API can return, plus recommended retry behavior.",
  alternates: { canonical: "/docs/api/errors" },
};

# Errors

Every error response has a stable JSON shape. You'll find an HTTP status
code, a machine-readable `type`, a human-readable `message`, and a
`request_id` for support correlation.

## Shape

```json
{
  "error": {
    "type": "invalid_request",
    "message": "source_type must be one of: paste, youtube, url, upload",
    "param": "source_type",
    "request_id": "req_01HW..."
  }
}
```

The `request_id` is also returned as the `X-Request-Id` response header on
every request (including successes).

## Error types

| Type              | Status | Meaning                                                     |
| ----------------- | ------ | ----------------------------------------------------------- |
| `invalid_request` | 400    | Validation failure (missing/invalid field).                 |
| `unauthorized`    | 401    | Missing, malformed, or expired API key.                     |
| `forbidden`       | 403    | Key is valid but doesn't own the target resource.           |
| `not_found`       | 404    | Resource does not exist (or isn't visible to your org).     |
| `conflict`        | 409    | State conflict (e.g., AI pass already running).             |
| `unprocessable`   | 422    | Semantically invalid (e.g., YouTube video has no captions). |
| `rate_limited`    | 429    | Per-org rate limit exceeded. Respect `Retry-After`.         |
| `server_error`    | 500    | Unexpected server failure. Safe to retry after a moment.    |
| `timeout`         | 504    | Sync wait exceeded the request's `timeout`.                 |

## When to retry

Retry `429`, `500`, and `504` with exponential backoff. Do not retry
`4xx` errors other than `429` — the request is malformed and will keep
failing until you change it.

On `504` from sync mode, the underlying job is still running. Switch to
polling with the returned `transcript.id` or wait for a webhook.

## Idempotency and retries

To safely retry POSTs without double-creating transcripts, send an
`Idempotency-Key` header. Any retry within 24 hours that uses the same
key returns the cached original response body and status, even if the
request body differs.

```bash
curl https://www.faithtranscripts.com/api/v1/transcripts \
  -H "Authorization: Bearer ft_live_YOUR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '...'
```

## Debugging with `request_id`

Every response includes `X-Request-Id`. If you file a support ticket or
hit something unexpected, include the request id. We can trace the
request through our logs, the deterministic pass, and BullMQ jobs from
that single value.
