Skip to main content
Reactor uses API keys to authenticate requests. JavaScript apps exchange the key for a short-lived token on their server; Python apps pass the key directly and the SDK does the exchange for them. Java apps exchange it explicitly with Reactor.fetchJwt, or use a token supplied by their backend.

Get your API key

1

Create an account

Sign up at reactor.inc.
2

Create an API key

Open the Dashboard, click your user icon, then navigate to API Keys to create a new key.
3

Copy your key

Your key starts with rk_. Store it securely and never commit it to source control.

JavaScript

How it works

Your server exchanges the API key for a short-lived token, which the browser uses to connect. Always mint session-scoped tokens: pass authorization_details naming the models the token may start sessions for. A scoped token can only create and operate its own sessions — it cannot touch other sessions, other models, or any account API. If it leaks, the blast radius is a handful of sessions on the models you listed, for at most the token’s lifetime (1 hour by default).
Server exchanges API key for a token, passes token to browser, browser connects to Reactor

Generate a session-scoped token

Exchange your API key for a short-lived token by making a POST request to the /tokens endpoint with your API key in the Reactor-API-Key header. Always scope the token with authorization_details, naming the models it may start sessions for:
Session-scoped (1-hour expiry)
Custom expiry
The authorization_details entry has these parts:
  • type: "session" (required) — the only supported type today. The token can create sessions and do everything those sessions need (transport negotiation, uploads, clips, session logs), and nothing else.
  • resources.models.match (required) — a non-empty list of models the token may start sessions for. Every listed model must be visible to your API key. There is no wildcard; list each model explicitly.
  • constraints.max_sessions (optional) — how many sessions the token may create in total, from 1 to 500. Defaults to 5. This counts sessions ever created by the token, not concurrent ones: closing a session does not restore capacity.
  • constraints.max_session_duration_seconds (optional) — cap every session the token creates at this many seconds, from 1 to 86 400 (24 hours). Omit for no limit; if the account also has a maximum, the lower value wins. See Rate limits.
  • resources.sessions.bind (optional) — a non-empty list of IDs of open sessions that the token did not create itself. Required when the token must act on such a session. See Acting on a session another token created.
Session-scoped tokens live for 1 hour by default. Pass expires_after (in seconds) to shorten or extend the lifetime, up to the server ceiling of 6 hours; values at or above the ceiling are silently clamped. The server still returns 200, so always check expires_at (a Unix epoch timestamp) on the response to confirm the actual expiry.
Omitting authorization_details mints an unscoped token that can call every API your key’s roles allow — sessions on any model, account data, key management. Never hand an unscoped token to a browser. Reserve unscoped tokens for trusted server-to-server calls (for example the Platform API), and don’t store your API key in client-side code either: use your server as a proxy to mint scoped tokens, as shown below.

Server-side proxy

Set up an API route on your server that mints a session-scoped token and returns it to your frontend:
Then fetch the token from your frontend and pass it to the SDK:
app/page.tsx

Acting on a session another token created

A session-scoped token can act on the sessions in its bound set: the sessions it created, plus any named in resources.sessions.bind when it was minted. Requests on a session outside that set return 403, including DELETE /sessions/{id}. To act on a session that a different token created, mint a token with bind naming that session, then use it:
The response from /tokens echoes authorization_details with the bound set and the resolved max_sessions. Bound IDs are stored on the server and do not appear in the JWT claims.Rules for bind:
  • Every ID in bind must name a session that is still open and that your account owns. A closed, foreign, or unknown ID returns the same 403, so the endpoint cannot be used to find session IDs.
  • The model of each bound session must appear in models.match. If it does not, the request returns 403.
  • max_sessions defaults to the number of bound sessions, so the token has no room left to create: it can operate the sessions it was given, but POST /sessions returns 403. Pass a larger max_sessions when the token also has to create sessions.

Ending a session you did not create

The JavaScript SDK deletes a session only from the client that created it (see Who owns the session). To end it from any other client or server, send DELETE /sessions/{id} with a token bound to that session as the bearer, as endSession above does. The JSON body with a reason is optional.A server process that holds the API key does not need a token. It can send the key itself as the bearer on DELETE /sessions/{id} and end any session under your account. Never do this from a front end. The API key must stay on your server.

Keeping the token fresh for a whole session

jwtToken also accepts a resolver — a function returning a string or a Promise<string> — instead of a static value. Pass one whenever a session might outlive a single connect: the SDK calls the resolver again for every later request that session makes (uploads, clip manifests, ICE refreshes, SDP renegotiation), not just the initial connect.A resolver that mints a fresh token on every call breaks the session the moment it runs: a session-scoped token can only act on the sessions it created, so a newly-minted token has none of them bound yet, and the next upload or clip request 403s. Cache the token yourself, and only mint again once it’s close to expiring:
Cache the token in your own code, not in the browser’s HTTP cache: fetch with { cache: "no-store" } rather than relying on a Cache-Control header to do the memoizing for you. The browser cache is outside your control — DevTools’ “Disable cache”, an eviction, or a shared proxy can all miss it silently, and the resolver falls back to minting a token with no sessions bound, 403ing every call the session makes from then on.
Edge case: a session created moments before the cached token expires is orphaned at the next refresh, because the freshly minted token is not bound to it. Re-mint with resources.sessions.bind naming that session, as shown in Acting on a session another token created.

Tokens for a queue or admission-control server

If you run an admission-control layer in front of a fixed pool of sessions — a waiting room gating limited GPU capacity, for example — mint a separate token per admitted slot instead of one shared token for the whole pool. Scope each token to max_sessions: 1 so it can create exactly the one session it is being admitted into:
Size slotLifetimeSeconds to the slot’s whole lifetime: the time an admitted user might take to connect, plus the full session duration. Omitting it defaults to 1 hour, and 6 hours is the ceiling; a session-scoped token can’t be topped up mid-session, so undersizing this strands the session it created. Hand the resulting jwt to the admitted client the same way as the server-side proxy above; its connect() call creates the session and binds it to this token’s grant.
If your API key is compromised, rotate it immediately from the Dashboard. Rotating does not affect active sessions. Need help? Email us at support@reactor.inc.
Adopting an existing session. If your backend creates a session and hands the sessionId to a client, that client must connect with a token bound to the session. See Acting on a session another token created and Sessions.