API Keys & Scopes
Granular API keys with per-resource read/write permissions, modeled on Cloudflare API tokens
Overview
Every API key carries a set of scopes — permissions of the form resource:permission — and can only do what its scopes allow. A key for a soundboard app might get lines:read and sfx:read only; a build pipeline that generates dialogue might add tts:generate and lines:write. Grant only what each integration needs.
Keys authenticate the v1 REST API, the MCP server, and the Public TTS API.
Scope Catalog
write always includes read for the same resource.
| Scope | Grants |
|---|---|
projects:read | List projects |
characters:read | List and read characters |
characters:write | Create and delete characters |
voices:read | List and read project voices |
voices:write | Create and delete project voices |
internal_voices:read | List and read Voice Studio custom voices |
internal_voices:write | Manage Voice Studio custom voices |
external_voices:read | List voice providers and their available voices |
lines:read | List, read, and download saved lines |
lines:write | Save lines, add versions, set active version, delete |
sfx:read | List, read, and download saved sound effects |
sfx:write | Generate and save sound effects, add versions, delete |
tts:generate | Generate speech with internal or external voices (spends tokens) |
Creating and Managing Keys
- 1Go to API Keys and choose New API Key.
- 2Name the key after the integration that will hold it ("Game build pipeline", "Support bot").
- 3Tick the permissions it needs — one row per resource with Read/Write checkboxes. At least one is required.
- 4Copy the key and store it in your secret manager. Treat it like a password.
Existing keys can be disabled (temporarily blocks all use), have their permissions edited at any time (takes effect immediately), or be deleted permanently.
Legacy keys
Keys created before scopes existed behave as if they had only tts:generate — exactly what they could do before. Edit their permissions to grant more.
Using a Key
Send the key with every request, preferably in the Authorization header:
# Preferred
curl -H "Authorization: Bearer $API_KEY" https://<host>/api/v1/verify
# Also accepted
curl -H "X-API-Key: $API_KEY" https://<host>/api/v1/verifyVerifying a key
GET /api/v1/verify works with any valid key (no scope required) and returns its name, organization, and scopes — useful in health checks and for debugging permission issues:
{
"success": true,
"result": {
"keyId": "…",
"name": "Game build pipeline",
"organizationId": "…",
"scopes": ["tts:generate", "lines:write", "lines:read"],
"status": "active"
}
}Errors and Limits
| Status | Meaning |
|---|---|
401 | Missing, invalid, or disabled API key |
403 | The key works but is missing the required scope (the error names it) |
402 | Insufficient token balance for a generation |
429 | Rate limited — 60 requests/minute per key (Retry-After header included) |
All v1 errors use the response envelope:
{ "success": false, "errors": [{ "code": "missing_scope", "message": "API key is missing the required scope: lines:write" }] }Best Practices
Recommendations
- • One key per integration — never share keys between apps.
- • Grant the minimum scopes; prefer read-only keys wherever possible.
- • Rotate by creating a new key, migrating, then deleting the old one.
- • Disable (rather than delete) a key to investigate suspicious usage.
- • Watch token spend in the Tokens dashboard — generation calls are attributed in the audit log.
