# Authentication

Every REST and WebSocket-starting request needs a bearer token:

```
Authorization: Bearer <token>
```

Wakili accepts two kinds of token in that header, auto-detected by shape:

| Token | Looks like | Use for |
|---|---|---|
| API key | `wak_...` | Server-to-server integration. Long-lived, scoped to your account, revocable individually. |
| Dashboard session token | Supabase JWT | The Wakili dashboard itself (browser login). Not meant for your own integration code. |

## API keys

Generate one from an agent's **Connect** tab in the dashboard, or via the [API keys API](/docs/api-keys-api). The raw key is shown exactly once at creation — store it immediately, Wakili only ever stores its hash afterward.

An API key authenticates as your account, not as a single agent — it can call any endpoint scoped to your account (create/list/delete agents, start calls on any of your agents, read usage and billing). Treat it like a database credential:

- **Never ship it to a browser.** Call the Wakili API from your own backend, and only forward the short-lived `ws_url` a call start returns to the client. See the [Next.js integration guide](/docs/nextjs-integration) for the exact pattern.
- **One key per integration/environment.** Makes revoking a compromised key (or rotating for a decommissioned service) not touch anything else.
- **Delete unused keys.** `DELETE /api-keys/{key_id}`.

## Listing and revoking keys

```bash
# list
curl https://api.wakili.dev/api-keys -H "Authorization: Bearer wak_..."

# revoke
curl -X DELETE https://api.wakili.dev/api-keys/{key_id} -H "Authorization: Bearer wak_..."
```

`GET /api-keys` never returns the raw key — only `id`, `name`, `key_prefix`, `last_used_at`, `created_at`, so you can identify a key without ever having it live again.

## The talk WebSocket doesn't take a token

`WS /calls/{call_id}/talk` takes no `Authorization` header and no token in the query string. Auth happens once, up front, on the authenticated `POST /agents/{agent_id}/calls` call that mints the `call_id` — the WebSocket just presents that single-use, short-lived id. This keeps credentials out of the WS URL entirely (query strings end up in logs, browser history, proxies).
