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

# API Authentication

API keys, scopes, headers, and the OAuth-style device flow for CLIs.

# API Authentication

Every v1 request needs an API key prefixed `riq_`. Pass it via either
header:

```bash X-API-Key
curl https://api.rogeriq.com/api/v1/projects/prj_123/conversations \
  -H "X-API-Key: riq_your_key"
```

```bash Authorization
curl https://api.rogeriq.com/api/v1/projects/prj_123/conversations \
  -H "Authorization: Bearer riq_your_key"
```

## Getting a key

Three paths:

1. **Dashboard** → Settings → API Keys → Create.
2. **CLI** → `rogeriq keys create my-key --scopes write`.
3. **Device flow** → for end-user CLIs / desktop apps, use
   [`POST /api/auth/device/code`](/api/device-auth) → user approves in
   browser → CLI polls for the key. The `rogeriq auth login` command is
   reference implementation.

## Scopes

See the full [scope reference](/api/scopes). TL;DR:

| Scope | Grants |
|-------|--------|
| `admin` | Everything. |
| `write` | All `*:read` + `*:write`. Not `*:admin`. |
| `read` | All `*:read`. |
| `<resource>:admin` | `:read` + `:write` + `:admin` on that resource. |
| `<resource>:write` | `:read` + `:write` on that resource. |
| `<resource>:read` | `:read` on that resource only. |

Method-based default: `GET` requires `read`, mutations require `write`.

## Organization binding

An API key is permanently tied to a single organization. The server
rejects any request whose path resolves to a project in a different org,
even if the key's owner is a member of that other org:

```json
{ "error": "API key is not authorized for this organization",
  "code": "API_KEY_ORG_MISMATCH" }
```

This means leaking one org's key can't pivot into another.

## Response headers

Every v1 response includes:

| Header | Use |
|--------|-----|
| `X-Request-Id` | Echoed in error bodies. Include when reporting issues. |
| `X-RateLimit-Limit` | Requests-per-minute allowed for this key's tier. |
| `X-RateLimit-Remaining` | Requests left in the current window. |
| `X-RateLimit-Reset` | Unix seconds when the window resets. |
| `X-RateLimit-Tier` | `starter`, `pro`, `scale`, or `enterprise`. |

## Error format

```json
{
  "error": "Conversation not found",
  "code": "CONVERSATION_NOT_FOUND",
  "request_id": "req_abc123",
  "issues": [],
  "retry_after": 30
}
```

`code` is a stable machine-readable enum — branch on it, not on
`message`. See the [errors page](/api/errors) for the full table.

## Troubleshooting 401 / 403

**401 — Authentication required**

Either no key was sent or the key was malformed. Make sure you're
    sending `X-API-Key: riq_...` or `Authorization: Bearer riq_...` exactly,
    no extra whitespace.
  **401 — Invalid API key**

The key doesn't exist (typo) or was revoked. Re-issue with
    `rogeriq keys create` or via the dashboard.
  **401 — API key has expired**

The key passed its `expires_at`. Create a new one and revoke the old
    one.
  **403 — INSUFFICIENT_SCOPE**

The key authenticated fine but lacks the required scope for the
    method or resource. The error body includes `required_scope` and
    `granted_scopes`. Create a new key with the right scopes, or use a
    different key.
  **403 — API_KEY_ORG_MISMATCH**

The key belongs to a different organization than the project in the
    URL. Use a key from the right org.

## Best practices

- Store keys in a server-side secret manager. Never bundle in widget /
  form / frontend / mobile.
- Separate keys per environment (production, staging, local).
- Use least-privilege scopes. An agent that only reads tickets needs
  `conversations:read`, not `admin`.
- Rotate keys after employee offboarding or vendor changes — revocation
  is instant.
- All actions are recorded in the [audit log](/reference/security) tagged
  with the API key id, name, and prefix.