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

# Authentication

> Bearer tokens, sandbox vs live, and idempotency.

## API keys

Parlay uses Stripe-style API keys. Every request carries a Bearer token in the `Authorization` header.

```bash theme={null}
Authorization: Bearer pk_sandbox_C4F36IBTB80rkVfp_HVgQIu9GxztHFlbUtpGWfWd1ZV7dtjkR
```

## Two environments

<Tabs>
  <Tab title="Sandbox">
    **Prefix:** `pk_sandbox_…`

    Sandbox keys hit the same infrastructure as live keys, against test data. AI-cost operations are billed at \$0 — use them freely while integrating.

    Mock recording URLs (`mock://perfect-pitch`, `mock://average-pitch`, `mock://poor-pitch`) return deterministic test analyses in under a second. Use these for unit tests and demos.

    **Base URL:** `https://api.goparlay.io`
  </Tab>

  <Tab title="Live">
    **Prefix:** `pk_live_…`

    Live keys hit production. Every AI operation is billed; webhooks fire to your registered endpoints; data is durable.

    Real recording URLs (HTTPS audio files, pre-signed S3/GCS URLs) trigger the full Deepgram + Gemini pipeline. Latency 30–90 seconds for typical calls.

    **Base URL:** `https://api.goparlay.io` (same as sandbox — gating is on the key prefix, not the URL)
  </Tab>
</Tabs>

<Tip>
  **Three names, one signal.** API responses + the MCP `echo_ping` expose three aliases for the same environment marker so you can use whichever suits your code:

  | Field         | Sandbox value   | Live value     | Where it appears                     |
  | ------------- | --------------- | -------------- | ------------------------------------ |
  | `key_prefix`  | `"pk_sandbox_"` | `"pk_live_"`   | The literal prefix on the key string |
  | `environment` | `"sandbox"`     | `"production"` | Stable enum (preferred for new code) |
  | `live`        | `false`         | `true`         | Convenience boolean                  |

  All three carry the same signal. `pk_live_…` ↔ `environment: "production"` ↔ `live: true`.
</Tip>

## Key safety

* **Server-side only.** Never embed an API key in a mobile app, browser, or anywhere a customer can extract it. Use a thin proxy on your backend instead.
* **One key per environment per service.** Don't share live keys across staging + prod; rotate immediately if leaked.
* **Rotate via the dashboard.** Revoking a key is instant — every request after that returns `key_revoked`.

## Required headers

| Header                           | Required on                          | Purpose                                                               |
| -------------------------------- | ------------------------------------ | --------------------------------------------------------------------- |
| `Authorization: Bearer <key>`    | Every request                        | Identifies the partner + scopes                                       |
| `Content-Type: application/json` | Mutating requests with a body        | Standard JSON body                                                    |
| `Idempotency-Key: <uuid>`        | All `POST`, `PATCH`, `PUT`, `DELETE` | Lets you safely retry on transient failures without duplicate effects |

## Idempotency

Generate a fresh UUID v4 per *logical* request. Retries with the **same** key return the cached response. A different key on the same payload creates a duplicate.

```javascript theme={null}
const key = crypto.randomUUID();

await fetch("https://api.goparlay.io/v1/analyses", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${KEY}`,
    "Idempotency-Key": key,                      // ← same key on retry
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ org_id, rep_id, recording_url }),
});
```

The MCP server handles idempotency for you — fresh UUID per tool call, automatic.

## Scopes

Sandbox keys can hit every read + mutate endpoint. Admin operations (creating partners, minting keys, suspending partners) require an admin key — those are not exposed via the MCP server intentionally.

## Common auth errors

| Code                       | Status | Meaning                                             |
| -------------------------- | ------ | --------------------------------------------------- |
| `authentication_required`  | 401    | Missing or malformed `Authorization` header         |
| `invalid_api_key`          | 401    | Key doesn't exist or is malformed                   |
| `key_revoked`              | 401    | Key was revoked via dashboard — rotate to a new one |
| `key_environment_mismatch` | 403    | Live key used against sandbox URL or vice versa     |
| `scope_required`           | 403    | Action requires a scope your key doesn't have       |

See the full [errors reference](/errors) for remediation guidance.
