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 |
|---|---|
organization:read | Read the organization and its art direction for 3D models |
organization:write | Edit the organization-wide art direction for 3D models |
projects:read | List projects |
projects:write | Create, edit, and delete projects |
characters:read | List and read characters |
characters:write | Create, edit, and delete characters |
internal_voices:read | List and read Voice Studio custom voices |
internal_voices:write | Manage Voice Studio custom 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 |
music:read | List, read, and download saved music |
music:write | Generate and save music, add versions, delete (spends credits) |
models:read | List, read, and download saved 3D models; read generation jobs and the model catalog |
models:write | Generate, refine, and delete 3D models; cancel generation jobs (spends credits) |
canvas:read | List, read, and download Canvas images (menu, HUD and in-game art); read the image catalog |
canvas:write | Generate, edit, and delete Canvas images (spends credits) |
live_endpoints:read | List and read live endpoints, and fetch their cached audio through the Public TTS API (never generates) |
live_endpoints:write | Create, edit, and delete live endpoints |
tts:generate | Generate speech with Voice Studio voices (spends credits) |
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 credit 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.
- •
models:writeis the most expensive permission — one job reserves up to a few hundred credits — so give it only to the integrations that actually generate 3D models; a build pipeline that just downloads finished models needsmodels:read. - • Anything you ship to players that talks to Tonetta at runtime (a game fetching live endpoints, a mobile app) gets its own read-only key —
live_endpoints:read, pluslines:read/sfx:readonly if it downloads saved content — so a leaked key can fetch existing audio but never create, delete, or spend credits. Keep the full-scope key for development; a build that only plays imported audio needs no key at all. The Unreal plugin has a slot for each. - • Rotate by creating a new key, migrating, then deleting the old one.
- • Disable (rather than delete) a key to investigate suspicious usage.
- • Watch credit spend in the Credits dashboard — generation calls are attributed in the audit log.