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:
bashcurl https://api.rogeriq.com/api/v1/projects/prj_123/conversations \ -H "X-API-Key: riq_your_key"
bashcurl https://api.rogeriq.com/api/v1/projects/prj_123/conversations \ -H "Authorization: Bearer riq_your_key"
Getting a key
Three paths:
- Dashboard → Settings → API Keys → Create.
- CLI →
rogeriq keys create my-key --scopes write. - Device flow → for end-user CLIs / desktop apps, use
POST /api/auth/device/code→ user approves in browser → CLI polls for the key. Therogeriq auth logincommand is reference implementation.
Scopes
See the full scope reference. 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 for the full table.
Troubleshooting 401 / 403
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.
The key doesn't exist (typo) or was revoked. Re-issue with
rogeriq keys create or via the dashboard.
The key passed its expires_at. Create a new one and revoke the old
one.
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.
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, notadmin. - Rotate keys after employee offboarding or vendor changes — revocation is instant.
- All actions are recorded in the audit log tagged with the API key id, name, and prefix.