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

# Authentication

> API-key auth, rate limits, scopes, and rotation for the Yotel public API.

<Info>
  **Building a partner app** that acts on behalf of individual Yotel
  users (Salesforce, HubSpot, etc.)? You want [OAuth 2.0](/authentication/oauth),
  not an API key. API keys are for *your own* tenant's automation.
</Info>

## Headers

Every request to `/api/v1/*` must send:

```
Authorization: Bearer yt_<env>_<secret>
```

Other headers:

| Header                           | Required              | Purpose                                                   |
| -------------------------------- | --------------------- | --------------------------------------------------------- |
| `Content-Type: application/json` | on writes             | request body type                                         |
| `Idempotency-Key: <uuid>`        | recommended on writes | dedup on retry (see [Idempotency](/concepts/idempotency)) |

## Environments

| Prefix      | Behavior                                                                                      |
| ----------- | --------------------------------------------------------------------------------------------- |
| `yt_live_…` | Real dialing, real PSTN charges, real webhooks to your production URL.                        |
| `yt_test_…` | Sandbox: DB writes happen, webhooks fire, but no PSTN originate. Use during development + CI. |

Environment is pinned on the key at issue time. You cannot promote a
test key to live or vice versa; issue a new key in the target
environment.

## Rate limits

Every key has a `rate_limit_per_min` — default 600, configurable up
to 100,000 for enterprise plans. The algorithm is a fixed per-minute
window:

```
Request lands at minute M → INCR counter for M → if count > limit, 429.
```

**Response headers on every 2xx:**

```
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 437
```

**On 429:**

```
HTTP/1.1 429 Too Many Requests
Retry-After: 43
```

`Retry-After` is seconds until the next minute boundary. Well-behaved
clients sleep for that duration and retry.

### Burst behavior

At the minute boundary, 2× the limit can pass in a single second
(last second of minute A + first second of minute B). This is a
known property of fixed-window counters; if you need exact 600/min
smoothing, throttle on your side with a client-side token bucket.

### Redis outage fallback

If our rate-limit backend is unreachable, we **fail open** — your
request isn't rate-limited. This is deliberate: a DoS control
shouldn't block paying customers during our own outages. We still
log the failure and rate-limit at the LB in emergencies.

## Scopes

Reserved for v1.1 — the `scopes` field on a key is accepted but not
enforced per-route yet. Safe to leave empty during v1. When scope
enforcement lands, existing keys with empty scopes will be grandfathered
into "all scopes granted."

## Rotating keys

Keys don't expire. To rotate:

<Steps>
  <Step title="Issue a new key from the dashboard">
    Give it a label like `Prod rotation 2026-Q2`.
  </Step>

  <Step title="Deploy it to your backend">
    Roll the env var, restart services, verify traffic on the new key
    via dashboard → Usage → filter by key prefix.
  </Step>

  <Step title="Revoke the old key">
    Dashboard → Keys → old key → Revoke (with a reason for the audit
    trail). Revocation is immediate; subsequent requests with the old
    key return 401.
  </Step>
</Steps>

<Warning>
  There's no grace period after revoke. Plan your cutover so the new
  key is fully deployed before you revoke the old one.
</Warning>

## Auth errors

```json 401 Malformed key theme={null}
{
  "detail": "Malformed API key"
}
```

Your `Authorization` value doesn't parse as `yt_<env>_…`. Common cause:
copy-paste lost the `Bearer ` prefix.

```json 401 Invalid key theme={null}
{
  "detail": "Invalid API key"
}
```

The prefix is parseable but no active key matches. Either the key was
revoked, mistyped, or never existed.

```json 403 Scope denied (v1.1) theme={null}
{
  "detail": "API key missing required scope: campaigns:write"
}
```

You'll start seeing this when scope enforcement ships. Add the required
scope to the key (or issue a new one with it).

```json 429 Rate limited theme={null}
{
  "detail": "Rate limit exceeded"
}
```

See rate limits section above.
