> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reactor.inc/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To build and serve your own model, start at /deploy/development/quickstart and /deploy/development/overview. Deploying is the default path: reactor init scaffolds a workspace, reactor auth login authenticates, and reactor model deploy registers the model, publishes the release with the weights/ folder, and activates it on Reactor's GPUs, in one command from that workspace. Docker must be running, because the publish step builds the image locally. Bump model.version in reactor.yaml before redeploying a change, because a release that already has an image is reactivated as it is. Deployment access is granted per account, so contact team@reactor.inc if a deploy is refused. Every key in reactor.yaml is documented at /deploy/platform/reactor-yaml. Model code imports reactor_runtime; Python client code imports reactor_sdk. The runtime overview explains the model interface. Running the model on your own machine with reactor run is optional and needs a GPU you attach with --gpus; /deploy/development/local-testing covers that loop and pairs a complete brightness model with a Python client test in a separate brightness-test workspace.
> Reactor hosts multiple models, each with its own connect slug (modelName) and command/event schema. The video model catalog — slug, typed SDK package, and links to its schema — is at /model-api-reference/overview. Robotics policy documentation starts at /robotics/overview; The launch policies are Cosmos Nano DROID (/robotics/cosmos/nano-policy-droid/), DreamZero DROID (/robotics/dreamzero/droid/), and FLUX Action DROID (/robotics/flux-action/droid/), organized by model family and embodiment. Some models expose one slug per experience (e.g. HappyOyster); always take the slug from the model's own pages, never guess it.
> Fastest path to a working app: `npx create-reactor-app my-app --model=<slug>` scaffolds a complete app with secure auth wired up. Typed TypeScript SDKs are published as @reactor-models/<model>; Python uses the base reactor-sdk package.
> Auth: exchange an API key (rk_...) for a JWT via POST https://api.reactor.inc/tokens from your server. Never put the API key in client-side code.
> Append .md to any docs URL for clean Markdown. Search these docs via the MCP server at https://docs.reactor.inc/mcp.

# Authentication

> Authenticate Platform API calls with an API key, and mint session tokens that can only start and operate sessions.

The Platform API accepts two credentials, both sent as a bearer token in the `Authorization` header:

* An **API key** (`rk_...`) is a long-lived secret. It works on every endpoint your key's roles
  allow. Keep it on a server.
* A **session token** is a short-lived JWT that you mint from an API key with `POST /tokens`. It
  works only on `/sessions` and `/clips`, and only for the models and sessions you name. Give it to
  a browser, a mobile app, or any client you do not control.

![Server exchanges API key for a token, passes token to browser, browser connects to Reactor](https://mintcdn.com/reactortechnologiesinc/di-lNvbv-_WwgH1P/diagrams/auth-browser.svg?fit=max\&auto=format\&n=di-lNvbv-_WwgH1P\&q=85\&s=a6f63e88dd29fc985828416c43e57f91)

## API keys

### Create an API key

Create keys in the [Dashboard](https://reactor.inc/dashboard): click your user icon, then open **API
Keys**. The full key appears only once, when you create it, so store it in a secret manager.

Each key is bound to one account: the account that was selected when you created it. To create a key
for a team, switch to the team first. See [API keys and billing](https://docs.reactor.inc/teams/keys-and-billing.md).

A key acts with your roles in that account, or with a subset you choose when you create it. If you
lose a role later, keys that relied on it lose it too.

> [!NOTE]
>
> You cannot create an API key with the Platform API using another API key or a session token. A
> credential that leaked cannot be used to create a replacement for itself. The `reactor auth login`
> command creates a key for the CLI through a browser sign-in; that key expires after 90 days.

### Send an API key

```bash theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
curl https://api.reactor.inc/me \
  -H "Authorization: Bearer $REACTOR_API_KEY"
```

The API tells an API key from a JWT by its `rk_` prefix. Older clients may send the key in a
`Reactor-API-Key` header instead. The API reads that header only when there is no `Authorization`
header. Do not use it in new code.

Some endpoints accept **only** an API key: everything outside `/sessions` and `/clips`, including
`/me`, `/accounts`, `/models`, `/projects`, `POST /tokens`, and `POST /files`.

### Revoke an API key

Revoke keys in the Dashboard. A key can also revoke itself:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
curl -X DELETE "https://api.reactor.inc/users/$USER_ID/api-keys/$API_KEY_ID" \
  -H "Authorization: Bearer $REACTOR_API_KEY"
```

`$USER_ID` is the `id` from `GET /me`. Revocation takes effect at once, and it also invalidates
every session token minted from the key.

> [!WARNING]
>
> Treat an API key like a database password. Never put it in a browser, a mobile app, an edge
> worker, or source control. Rotate keys from time to time, and revoke a key as soon as you suspect
> it leaked.

## Session tokens

A session token lets a client start sessions without holding your API key. It can call these
endpoints and nothing else:

* `POST /sessions`, to start a session on a model the token allows.
* Every `/sessions/{session_id}/...` endpoint, for sessions the token created or was bound to. That
  covers reading and ending a session, WebRTC signaling, uploads, and step results.
* `GET /clips`, for recordings of those sessions, also after a session ends.

Other endpoints reject session tokens with `403`, except `POST /tokens` and `POST /files`.
Those two endpoints return `401`.

### Mint a session token

Send your API key to `POST /tokens`. With no body, the token can start up to 5 sessions on any model
your key may use, for 1 hour:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
curl -X POST https://api.reactor.inc/tokens \
  -H "Authorization: Bearer $REACTOR_API_KEY" \
  -H "Reactor-API-Version: 1"
```

```json theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
{
  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": 1784375400,
  "authorization_details": [
    {
      "type": "session",
      "resources": { "models": { "all": true } },
      "constraints": {}
    }
  ]
}
```

The field is `jwt`, not `token`. `expires_at` is a Unix timestamp in seconds. An empty `constraints`
object means the defaults apply, including the limit of 5 sessions.

### Limit what a token can do

Before you hand a token to a client, limit it to what that client needs. Pass one
`authorization_details` entry:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
curl -X POST https://api.reactor.inc/tokens \
  -H "Authorization: Bearer $REACTOR_API_KEY" \
  -H "Reactor-API-Version: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "expires_after": 900,
    "authorization_details": [
      {
        "type": "session",
        "resources": { "models": { "match": ["reactor/helios"] } },
        "constraints": { "max_sessions": 1, "max_session_duration_seconds": 600 }
      }
    ]
  }'
```

This token lives 15 minutes. It can start one session, only on `reactor/helios`, and that session
ends after 10 minutes.

| Field | Type | Description |
| - | - | - |
| `expires_after` | integer | Token lifetime in seconds. Defaults to 3600 (1 hour). The maximum is 6 hours, and a token never outlives the API key that minted it. Read `expires_at` in the response for the actual expiry. |
| `authorization_details` | array | Exactly one entry. |
| `authorization_details[].type` | string | Required. Always `"session"`. |
| `resources.models.match` | array of strings | The models the token may start sessions on, as `owner/name`. Use `owner/name:environment` to allow a non-production environment instead. Every model must be one your key can use. Set either `match` or `all`, not both. |
| `resources.models.all` | boolean | `true` allows every model your key can use. |
| `resources.sessions.bind` | array of UUIDs | Open sessions the token may act on although it did not create them. See [Share a session](#share-a-session-with-another-client). |
| `constraints.max_sessions` | integer | How many sessions the token may hold over its lifetime, from 1 to 500. Defaults to 5, or to the number of bound sessions. Ending a session does not free its slot. |
| `constraints.max_session_duration_seconds` | integer | Ends every session the token starts after this many seconds, from 1 to 86400. Omit it for no limit from the token. If your account also has a session time limit, the lower value applies. |

The response repeats `authorization_details` in canonical form: model names are resolved, sorted,
and deduplicated. It includes `max_sessions` when you supply a limit or bind sessions.
Otherwise, it omits `max_sessions`, and the default limit of 5 sessions applies.

### Use a session token

Send the token exactly as you would an API key:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
curl -X POST https://api.reactor.inc/sessions \
  -H "Authorization: Bearer $REACTOR_JWT" \
  -H "Reactor-API-Version: 1" \
  -H "Content-Type: application/json" \
  -d '{"model": {"name": "reactor/helios"}}'
```

Each session the token starts is bound to it at once, so the same token can then negotiate
transport, upload files, and end that session.

### Share a session with another client

A token can act only on the sessions it started. To let a second client join a session that already
exists, mint a separate token for that client and bind the session to it:

```json theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
{
  "authorization_details": [
    {
      "type": "session",
      "resources": {
        "models": { "match": ["reactor/helios"] },
        "sessions": { "bind": ["1f1e4b8e-1c2d-4f8a-9b6e-2a7c5d3e9f01"] }
      }
    }
  ]
}
```

* Each bound session must be open and must belong to your key's account and user.
* Each bound session must run a model listed in `resources.models.match`.
* Bound sessions count toward `max_sessions`. When you omit `max_sessions`, the limit equals the
  number of bound sessions, so the token cannot start new sessions. Set `max_sessions` higher to let
  it start more.

A session that is closed, belongs to someone else, or does not exist returns `403`. The three cases
look the same, so the endpoint never reveals whether a session ID exists.

### Errors

| Status | When |
| - | - |
| `400` | The body is malformed, a field inside `authorization_details` is unknown, or a value is out of range. |
| `401` | The API key is missing, invalid, revoked, or expired. A session token instead of an API key also returns `401`: a token cannot mint a token. |
| `403` | Your key cannot use a listed model, cannot bind a listed session, or has no roles left that allow sessions. |
| `426` | `Reactor-API-Version` is older than the server supports. |
| `503` | Token issuance is temporarily unavailable. Retry with backoff. |

A session token that has expired, or whose API key was revoked, returns `401` on every call. A
session token used outside its limits returns `403`: another session, a model it does not allow, or
a new session beyond `max_sessions`. Other endpoints reject session tokens with `403`, except
`POST /tokens` and `POST /files`, which return `401`.

## Accounts and roles

Every request acts in one account: the account the API key is bound to. Session tokens inherit it.
To act in another account, use a key created in that account.

What a credential may do depends on its roles in that account:

| Role | Can |
| - | - |
| `developer` | Start sessions, read the account's sessions and usage, and manage the account's models |
| `account_admin` | Everything a developer can, plus manage members, invitations, and model access grants |
| `billing_admin` | Read the account and its members, and manage billing in the Dashboard |

Each endpoint page names the roles it needs. A credential without them gets `403`.

## Next steps

> [card] [Mint a session token](https://docs.reactor.inc/platform-api/tokens/create-a-session-token.md)
>
> The full `POST /tokens` reference, with a playground.

> [card] [Client authentication](https://docs.reactor.inc/authentication.md)
>
> Mint tokens from a server and hand them to the JavaScript, Python, and mobile SDKs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.