# Authentication

Raven accepts three kinds of credentials. All are sent as `Authorization: Bearer <token>`. For an API key, `X-Subscription-Token: <key>` also works.

| Credential | Looks like | Get it from | Use it for |
|---|---|---|---|
| API key | `rvn_live_…` | The dashboard, the CLI, or the device flow | Scripts, the REST API, MCP clients without OAuth |
| OAuth access token | `rvn_at_…` (expires in 1 hour) | An MCP client's OAuth sign-in | MCP clients such as Claude Code, Codex and Gemini CLI |
| Browser session | a cookie | Signing in at ravenoutreach.com | The dashboard |

## API keys

An account can have up to 10 keys. Only a hash of each key is stored, so a key is shown once, when it's created. Keys have the `read` and `write` scopes. Revoke a key in the dashboard. A tool holding a key can also revoke it itself, with `POST /oauth/revoke token=<key>`.

Never put a key in code or a commit. Keep it in a gitignored `.env` as `RAVEN_API_KEY`, in your OS keychain (the CLI does this), or in your agent's secret store.

## Device flow

The device flow ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628)) is how a CLI or coding agent signs a user up or in. The user clicks one link in a browser, and the agent receives an API key. A new account gets the free credit.

```sh
curl -s -X POST https://api.ravenoutreach.com/oauth/device/code \
  -d client_id=my-agent -d "client_name=My agent on $(hostname -s)"
```

```json
{"device_code": "rvn_dc_…", "user_code": "WDJB-MJHT",
 "verification_uri": "https://ravenoutreach.com/activate",
 "verification_uri_complete": "https://ravenoutreach.com/activate?code=WDJB-MJHT",
 "expires_in": 900, "interval": 5}
```

Show the user `verification_uri_complete` and the `user_code`. The approval page shows the same code, so they can check it matches. Then poll the token endpoint every `interval` seconds:

```sh
curl -s -X POST https://api.ravenoutreach.com/oauth/token \
  -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
  -d device_code=rvn_dc_… -d client_id=my-agent
```

| Response | Meaning |
|---|---|
| `{"error": "authorization_pending"}` | Keep polling. |
| `{"error": "slow_down"}` | Poll 5 seconds less often. |
| `{"error": "access_denied"}` | The user declined. Stop. |
| `{"error": "expired_token"}` | 15 minutes passed. Start again. |
| `{"access_token": "rvn_live_…", "token_type": "Bearer", …}` | Done. The key is handed out once. |

## OAuth for MCP clients

MCP clients that support [MCP authorization](https://modelcontextprotocol.io/specification/latest/basic/authorization) need only Raven's MCP URL. The flow:
1. The client calls `https://api.ravenoutreach.com/mcp` without a token and gets a `401`. Its `WWW-Authenticate` header points to the resource metadata.
2. The client registers itself and sends the user to a consent page.
3. The user approves with one click. If they're new, they sign up first.

| | |
|---|---|
| Protected resource metadata | `https://api.ravenoutreach.com/.well-known/oauth-protected-resource/mcp` |
| Authorization server metadata | `https://api.ravenoutreach.com/.well-known/oauth-authorization-server` |
| Registration | Dynamic client registration ([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)) at `/oauth/register`; public clients only (no secret) |
| Grant | Authorization code with PKCE (`S256` only); responses include `iss` ([RFC 9207](https://www.rfc-editor.org/rfc/rfc9207)) |
| Redirect URIs | `https` anywhere, `http` only for loopback addresses (any port), or a desktop app's own scheme |
| Scopes | `read` (account, teams, leads), `write` (create and steer teams, which spends credit) |
| Resource indicators | `https://api.ravenoutreach.com/mcp` (the default) or `https://api.ravenoutreach.com` (the REST API); a token works only at the resource it was issued for |
| Tokens | Access tokens last 1 hour. Refresh tokens last 30 days and rotate on every use. Replaying a used refresh token revokes the whole chain. |
| Revocation | `POST /oauth/revoke` ([RFC 7009](https://www.rfc-editor.org/rfc/rfc7009)); users can also disconnect apps in the dashboard |
