Authentication and keys
How API keys work, which header to send, what each scope grants, and how to rotate one.
Every request carries an API key as a bearer token. There is no OAuth flow, no session, and no signing step for requests you send us, only for webhooks we send you. Keys are created, scoped and revoked from the dashboard; there is no endpoint that mints one, because an API that can create its own credentials cannot be contained by revoking one.
Keys look like ptt_ followed by 40 hex characters. Only a SHA-256 hash is stored, so the raw key is shown once at creation and is unrecoverable afterwards. That call is also the cheapest way to check a key: a 200 returns the plan, the credit balance and scopes, so you can confirm what the key actually carries.
Unknown, revoked and expired all return the same 401. Deliberately: distinguishing them would confirm which keys exist. If a key stops working, check the dashboard rather than inferring from the response.
Scopes
A key carries the scopes you chose when creating it. An endpoint refuses with 403 insufficient_scope if any required scope is missing: a read-only key cannot spend a credit or publish a page, whichever door it comes through. Give an agent the narrowest key that does its job.
| Scope | Grants | Plan |
|---|---|---|
transcriptions:read | List, fetch, export, captions, usage, podcast lookup | Free |
transcriptions:write | Create and delete transcriptions, presigned uploads | Paid |
shows:read | List connected shows and webhook endpoints | Free |
shows:write | Publish episodes, manage webhook endpoints | Paid |
ai:write | Show notes, chapters and translation, over both REST and MCP | Paid |
A 403 plan_required is the other refusal, and it has two causes: the plan has no API access at all, or it has read access but not write. The scope check runs first, so a key failing both is told about the scope, that is the one you can fix without spending money.
Handling keys
- Name a key after where it runs: "staging worker", "Claude Desktop". When you revoke one in a hurry you will be choosing from a list of names.
- Copy it at creation. It is displayed once, and only the hash is stored.
- Environment variable or a secret manager; never in source control, and never in a client-side bundle, since anything named
NEXT_PUBLIC_*,VITE_*orREACT_APP_*is public. - One key per environment and per workload, so rotating or exhausting one does not touch the others.
- A key can carry an expiry date, after which it 401s like any other unusable key, which is useful for a contractor or a demo.
To rotate: create the replacement with the same scopes, deploy it and confirm traffic on it (last_used_at updates on every authenticated request), then revoke the old one. Both are valid at once, so there is no gap.
If a key leaks, revoke it, do not just stop using it. Deleting the line that referenced it changes nothing; the key authenticates until revoked. Revoke first, investigate second. Rewriting git history does not un-publish a committed key.