Authentication

Two credentials reach the same API. Your own code sends an API key as a bearer token; a connected agent gets an OAuth 2.1 access token instead, which is why a connect link carries no secret and is safe to publish. Both arrive on the same header and are told apart by prefix.

Last updated

API keys

For your own code. Created in the dashboard, sent as a bearer token, and shown exactly once.

The header, on every requesthttp
Authorization: Bearer sk_live_…
A complete authenticated callbash
curl -X POST https://api.tryplayhead.com/v1/frames \
  -H "Authorization: Bearer $PLAYHEAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'

The SDK reads PLAYHEAD_API_KEY from the environment when no key is passed, so a correctly configured process never has one in its source.

OAuth for connected agents

An agent does not get a key. It gets an access token, granted by you through a consent screen, and that difference is why a connect link can be published.

The remote MCP server implements OAuth 2.1 with dynamic client registration. A client discovers the endpoints from /.well-known/oauth-authorization-server, registers itself, sends you to a consent screen that names the scopes, and receives a token it sends on the same Authorization header a key would use.

Both credential spaces share that header and are told apart by prefix, so a token can never be looked up in the key table or the reverse.

The two credentials compared
API keyOAuth token
ForYour own codeA connected agent
Created byYou, in the dashboardA consent screen, once
Shapesk_live_…An opaque access token
BillsThe organization that owns the keyThe user who approved it
Revoked byDeleting it in the dashboardDisconnecting the client

Scopes

A consent screen names these, and an agent granted less than it needs is refused with insufficient_scope rather than with a generic failure.

OAuth scopes
ScopeAllows
video:readAnalysing videos and uploading files. Uploading shares this scope because an upload only exists to be watched.
account:readReading the plan, the balance and recent jobs.

Keeping a key safe

Three rules, and the third is the one that gets skipped.

  1. Never ship a key to a browser. A key in client-side JavaScript is a key anybody can read. Call the API from your server and proxy the result.
  2. Use the environment, not the source. PLAYHEAD_API_KEY is read automatically by the SDK, so there is no reason for a key to appear in a repository.
  3. Rotate before you delete. Several keys can be active at once. Create the replacement, deploy it, and only then delete the old one — otherwise the rotation is an outage.

Questions

Can I recover a lost API key?
No. Keys are shown once and stored only as a hash, so there is nothing to recover from. Delete the old one and create a new one — which takes about ten seconds and is also the correct response to a key that may have leaked.
Why do the connect links contain no key?
Because they could not. We store key hashes, so a link with a key in it is impossible to generate. The connection authenticates by signing in to your Playhead account instead, which is what makes those links safe to publish.
Which balance does a credential spend?
GET /v1/credits reports a wallet field saying exactly that. Plan credits are the monthly grant and reset; top-up credits are bought and never reset. A key billed from bought credit cannot spend the plan grant at all.
How do I rotate a key without downtime?
Create the new key, deploy it, then delete the old one. Several keys can be active at once, so there is no window where neither works.