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

# Authentication

> Create API keys in Soundlink, then use them with scopes, expiration, and Authorization Bearer.

## Create an API key

Organization **owners** and **admins** can create keys in the Soundlink app — no support ticket required.

<Card title="Open API keys in Settings" icon="key" href="https://getsoundlink.com/home">
  Sign in at Soundlink, open your profile menu → **Settings**, then **Developer
  → API keys**. The app uses your current organization automatically — no org ID
  in the URL.
</Card>

<Steps>
  <Step title="Open the API keys page">
    From your profile menu, go to **Settings → Developer → API keys**. Soundlink scopes this page
    to your active organization — you never paste an org ID into the URL.
    You'll see every key for the org — active, expired, and revoked — with scopes, expiration,
    and last used.

    <Frame caption="API keys list in Settings → Developer">
      <img src="https://mintcdn.com/soundlink/KlliMmjVHYTSKvtB/images/api-keys/list.png?fit=max&auto=format&n=KlliMmjVHYTSKvtB&q=85&s=a444461f2f042cd10203521be3f68cf5" alt="Soundlink Settings Developer API keys table with name, scopes, expiration, status, and Create key button" width="2934" height="1668" data-path="images/api-keys/list.png" />
    </Frame>
  </Step>

  <Step title="Create a key">
    Click **Create key**. Give the key a **name** (required), choose an **expiration**, and select
    one or more **scopes** (at least one required).

    | Field          | Details                                                                                                                                       |
    | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
    | **Name**       | A label you'll recognize later (e.g. `Warehouse sync`, `Staging`).                                                                            |
    | **Expiration** | Pick one: **7 days**, **30 days**, **90 days**, **1 year**, or **Never**. When the date passes, the API returns `401` with `api_key_expired`. |
    | **Scopes**     | Any combination of the v1 scopes below — at least one required. See [Scopes](#scopes-v1).                                                     |

    <Frame caption="Create key — name, expiration, and scopes">
      <img src="https://mintcdn.com/soundlink/KlliMmjVHYTSKvtB/images/api-keys/create-dialog.png?fit=max&auto=format&n=KlliMmjVHYTSKvtB&q=85&s=bdda1590a290793338864982e0095fb0" alt="Create API key dialog with name field, expiration dropdown, and scope checkboxes" width="2934" height="1668" data-path="images/api-keys/create-dialog.png" />
    </Frame>

    <Tip>
      Start with **30 days** and both read scopes if you're exploring the API. Rotate or revoke the
      key when you're done testing.
    </Tip>
  </Step>

  <Step title="Copy and store the key">
    After you click **Create key**, Soundlink shows the **full key exactly once**. Copy it
    immediately and store it in your secrets manager or password vault.

    <Warning>
      You cannot retrieve the full key later — only revoke it and create a new one. The keys list
      never shows the secret, only a masked placeholder.
    </Warning>

    <Frame caption="One-time key reveal — copy before closing">
      <img src="https://mintcdn.com/soundlink/KlliMmjVHYTSKvtB/images/api-keys/key-reveal.png?fit=max&auto=format&n=KlliMmjVHYTSKvtB&q=85&s=3b9e3a7233b3180c7a343617afc5ac10" alt="Save your API key dialog with masked key, reveal and copy buttons, and warning that the key is shown only once" width="2934" height="1668" data-path="images/api-keys/key-reveal.png" />
    </Frame>
  </Step>
</Steps>

Members (non-admin roles) see **API keys** in Settings but cannot open the page — ask an owner or
admin to create a key for your integration.

***

## API key format

```
sk_<prefix>_<secret>
```

Pass the full string in the **`Authorization`** header as a Bearer token (recommended):

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
-H 'Authorization: Bearer sk_abc123_...'
```

<Warning>
  The **`x-api-key`** header is deprecated and retiring **2026-08-17**. It still works today, but
  migrate to `Authorization: Bearer` above before then:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
  -H 'x-api-key: sk_abc123_...'
  ```
</Warning>

Send exactly one of the two — sending both, or an unrecognized `Authorization` value, returns `401` with code `mixed_credentials`. Do **not** send Firebase Bearer tokens or legacy product headers on this host.

## Scopes (v1)

Scopes limit which endpoints a key can call. Select them when you create the key; they cannot be changed without minting a new key.

| Scope             | Access                                                               | Self-serve UI |
| ----------------- | -------------------------------------------------------------------- | ------------- |
| `campaigns:read`  | List campaigns, campaign detail, strategy catalog, campaign tiers    | Yes           |
| `metrics:read`    | Metrics overview, breakdown, exports                                 | Yes           |
| `campaigns:write` | Create/stop wallet campaigns, budget increase/decrease, tier updates | Yes           |
| `videos:write`    | Import partner videos for Full Control creatives                     | Yes           |

Typical read-only integrations enable **both** `campaigns:read` and `metrics:read`.
Add `campaigns:write` only when the integration should spend wallet credit or manage delivery.

<Note>
  `GET /v1/ping` accepts any valid v1 key regardless of which read scopes you
  selected — useful for smoke tests after creation.
</Note>

## Expiration

Choose a lifetime at creation:

| Preset  | Duration                                  |
| ------- | ----------------------------------------- |
| 7 days  | Short-lived (experiments, CI)             |
| 30 days | Default-friendly for most integrations    |
| 90 days | Longer-running services                   |
| 1 year  | Maximum fixed preset in the app           |
| Never   | No expiration — revoke manually when done |

<Tip>
  Prefer a fixed preset over **Never** for anything you can rotate. A key that never expires only
  goes away when someone remembers to revoke it.
</Tip>

After `expiresAt`, requests fail with `401` and `api_key_expired`. Keys created with **Never** have
no `expiresAt` and only stop working when revoked. Create a replacement key before a fixed-lifetime
key lapses, update your integration, then revoke the old key.

## Organization isolation

Your organization is determined **only** from the API key. The API ignores `x-organization-id` and similar spoofed headers.

## Key lifecycle

* **Active** — key is within its expiration window and not revoked.
* **Revoked** — you deleted the key in Settings; API returns `api_key_revoked`.
* **Expired** — past `expiresAt`; API returns `api_key_expired`.

Revoked keys stay visible in the list with status and date so you can audit past integrations.

## Ping vs data endpoints

| Endpoint                                    | Scope required                |
| ------------------------------------------- | ----------------------------- |
| `GET /v1/ping`                              | Valid key only (any v1 scope) |
| `GET /v1/campaigns`, `/v1/strategies`, etc. | `campaigns:read`              |
| Metrics overview / breakdown / exports      | `metrics:read`                |
| Create / stop / budget / tier write routes  | `campaigns:write`             |
| Video import routes                         | `videos:write`                |

## Next

Verify your new key in [Quickstart](/docs/quickstart) with `GET /v1/ping`, then list campaigns.
