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

# API authentication

> How to authenticate with the SOAX API using API keys: the Authorization header, scopes, package restrictions, and key lifecycle.

The SOAX API authenticates every request with an **API key** sent as a bearer token:

```bash theme={null}
curl https://api.platform.soax.com/v1/account \
  -H "Authorization: Bearer s_live_YOUR_API_KEY"
```

API key secrets start with `s_live_`. A missing, malformed, unknown, or revoked key returns:

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

<Note>
  **API key ≠ package key.** The API key (`s_live_…`) authenticates calls to `api.platform.soax.com`. The package key (a 10-character password, one per package) authenticates proxy traffic through `proxy.soax.com:1337` — that's the `password` field in [connection strings](/api/packages/connection-string) and the value rotated by [rotate package password](/api/packages/rotate-password). They are managed separately and are not interchangeable.
</Note>

## Getting a key

Create keys in the dashboard under [**Settings → API keys**](https://platform.soax.com/settings/api-keys): click **Generate new API key**, choose a name and scopes, and optionally restrict the key to specific packages. The secret is displayed **once**, at creation — store it securely; it cannot be retrieved later. (Only a hash is stored server-side.)

You can also create keys programmatically with [`POST /v1/api-keys/`](/api/keys/create) — see [Key lifecycle](#key-lifecycle) below.

## Scopes

Each key carries a set of scopes, and each endpoint requires one. Calling an endpoint without the required scope returns:

```json theme={null}
// 403 Forbidden
{ "detail": "Missing required scope: proxy:packages:read" }
```

| Scope                     | Grants access to                                                                                            |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `proxy:packages:read`     | List/inspect packages, connection strings, and all location endpoints                                       |
| `proxy:packages:write`    | [Rotate package passwords](/api/packages/rotate-password)                                                   |
| `proxy:analytics:read`    | All [analytics endpoints](/api/analytics/summary)                                                           |
| `account:read`            | [Account](/api/account/get), [credits](/api/account/credits), and [subscription](/api/account/subscription) |
| `api-keys:read`           | [List API keys](/api/keys/list)                                                                             |
| `api-keys:write`          | [Create](/api/keys/create) and [revoke](/api/keys/revoke) API keys                                          |
| `team:read`, `team:write` | Reserved — no v1 endpoints use these yet                                                                    |

[`GET /v1/api-keys/me`](/api/keys/me) is the one exception: any active key can call it, regardless of scopes. Use it to check what a key can do.

Grant each key only the scopes it needs. A monitoring job that reads analytics needs `proxy:analytics:read` and nothing else.

## Package restrictions

A key can be restricted to specific packages (`package_ids`). A restricted key:

* only sees those packages in [`GET /v1/proxy/packages`](/api/packages/list),
* gets **404** (not 403) when requesting any other package — so the existence of packages outside the restriction is never revealed,
* has its [analytics](/api/analytics/summary) results limited to those packages, even when it passes a broader `package_ids` filter.

A key with `package_ids: null` is unrestricted and can access every package in the organization.

## Key lifecycle

Keys can manage keys, which lets you automate rotation without touching the dashboard:

* **Inspect** — [`GET /v1/api-keys/me`](/api/keys/me) shows the calling key's scopes, restriction, and rate-limit quota.
* **Create** — [`POST /v1/api-keys/`](/api/keys/create) (requires `api-keys:write`). To prevent privilege escalation, the new key's scopes must be a subset of the caller's, and its package restriction a subset of the caller's restriction.
* **Revoke** — [`DELETE /v1/api-keys/{key_id}`](/api/keys/revoke) (requires `api-keys:write`). Revocation is immediate and permanent: the key remains visible in listings with status `revoked`, but every request using it fails with 401. A key can revoke itself.

To rotate a key with zero downtime: create the replacement, deploy it, then revoke the old key.

## Best practices

* Store secrets in a secrets manager or environment variables, never in code or client-side bundles.
* Use separate keys per service or environment, so one leak doesn't require rotating everything and usage stays attributable (each key tracks `last_used_at`).
* Scope keys down: read-only keys for dashboards and monitoring, `api-keys:write` only where rotation is automated.
* Enable [two-factor authentication](/dashboard/account-billing#two-factor-authentication-2fa) on dashboard accounts that can manage API keys.
