When to use an API key
Use an API key when:- Your backend needs to call Oshara endpoints (sync transcripts into your CRM, build a customer-facing dashboard, automate reports).
- You want to authenticate without managing a user’s JWT refresh cycle.
- You need to revoke access independently — disable one key without rotating user passwords.
What a key can do
An API key inherits the permissions of the user who created it. It can read any data the owning user can read:| Endpoint | What the key can access |
|---|---|
Session History — GET /api/billing/usage/ | Sessions for any AI character the key’s owner created |
Chat History — GET /api/billing/sessions/<id>/ | Full transcript + recording URL for owned-character sessions |
Form Responses — GET /api/agents/<slug>/form-responses/ | Form submissions for owned characters |
404 (or empty results on list endpoints) — keys never see another user’s sessions.
Sending the header
X-API-Key and x-api-key work (HTTP headers are case-insensitive); use the capitalised form in docs and code.
When a request carries both a valid X-API-Key and a JWT, the API key wins — auth resolves to the API key’s user.
Create a key
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | ✓ | Human-readable label. Pick something that tells your future self where the key is used ("crm-sync", "staging-dashboard"). |
is_active | boolean | Defaults to true. Set false to create a disabled key. |
Response (201 Created)
The full key value is returned only once — store it immediately.
List your keys
key value is not included — only a masked preview. If you lost a key, create a new one and revoke the old.
Response
| Field | Description |
|---|---|
key_preview | First 5 + last 4 chars of the key, e.g. sk_a1...f6789. Useful for matching keys to environments. |
last_used_at | Timestamp of the most recent successful request. null if never used. Updated on every authenticated call. |
is_active | If false, the key is rejected. |
Revoke a key
Disable a key without deleting it (preferred — keeps the audit row):401 Unauthorized on subsequent requests.
Rename a key
Security guidance
- One key per integration. If
staging-dashboardis compromised, you revoke only that key —crm-synckeeps working. - Never embed in browser code. Keys give full read access to all your sessions/transcripts. Keep them server-side.
- Store as a secret. Use a secret manager (AWS Secrets Manager, HashiCorp Vault, GitHub Actions secrets). Don’t commit to git.
- Rotate periodically. Create the new key, deploy it, then revoke the old one — overlapping rotation, no downtime.
- Monitor
last_used_at. A key withlast_used_atgoing stale or appearing from an unexpected IP is a signal to investigate.
Errors
| Status | Cause | Fix |
|---|---|---|
401 Unauthorized | Missing header, unknown key, or is_active: false | Check the header is exactly X-API-Key and the key is active |
404 Not Found on a session/character endpoint | Key’s owner doesn’t own that character | Use a key owned by the character’s creator, or grant access via the dashboard |
200 with empty results | Same as above, on list endpoints | Confirm character_slug filter matches a character the owner created |
Code examples
Node.js (server-side)
Python (server-side)
Sync new sessions hourly
Related
- Session History — list sessions by character
- Chat History — single-session transcript + recording
- Form Responses — form submissions
- Authentication — JWT (the other auth scheme)
