APIs

APIs

Choose between the Control plane API and the Engine API.


rstream exposes two HTTP surfaces. The Control plane API lives under https://rstream.io/api/... and covers managed project discovery, hosted platform operations, and TURN issuance. The Engine API lives under https://<engine>/api/... and covers operational state inside a specific tunnels project.

Most integrations are better served by an SDK than by raw HTTP. The SDKs already handle project resolution, application credentials, short-lived tokens, and the split between hosted and self-hosted deployments. This page is intended for the smaller set of cases where raw HTTP remains useful, such as lightweight automation, custom runtimes without an SDK, or direct inspection of the wire-level API.

Choosing the right surface

The Control plane API is the right surface for whoami, project listing, project-endpoint resolution, managed TURN issuance, and other operations that exist only in the managed platform. The Engine API is the right surface for listing tunnels and clients, subscribing to state.initial, client.*, and tunnel.* events, and working against self-hosted deployments where no hosted Control plane API exists.

In hosted deployments, the common path is to call the Control plane API first to discover or resolve the project, then call the Engine API for operational work. In self-hosted deployments, the Engine API is often the only HTTP surface available.

Control plane API

The Control plane API is the API behind project discovery and other managed platform operations. Not every dashboard route should be treated as a stable public contract, which is why the recommended entrypoint is usually the JS SDK or the Go SDK. The operational routes below are the stable surface for lightweight automation and SDK-adjacent integrations.

The machine-readable OpenAPI contract is published at /api/openapi.json and advertised from /.well-known/api-catalog.

GET  /api/whoami
GET  /api/projects/tunnels
GET  /api/workspaces/<workspace-id>/projects/tunnels/plan/config
POST /api/workspaces/<workspace-id>/projects/tunnels
GET  /api/projects/tunnels/resolve/<project-endpoint>
POST /api/projects/tunnels/<project-id>/turn-server/credentials
POST /api/projects/tunnels/resolve/<project-endpoint>/turn-server/credentials

This layer is the part of the platform that knows about workspaces, managed projects, hosted plan state, and platform-issued TURN credentials.

Project creation is intentionally a preview-confirm flow. First, call the plan configuration endpoint for the workspace. It returns available and unavailable plans, provider/region choices, a recommended region when geolocation is available, billingImpact, billingAction, and a creationFingerprint.

The server selects the creation action through billingAction; browsers and agents should not choose a billing mode themselves. When the returned action can be confirmed directly, call the project creation endpoint with the selected plan, provider, region, the selected creationFingerprint, and an idempotencyKey. If the action requires hosted payment or workspace billing setup, hand the user to the hosted rstream flow instead of implementing billing routes directly. If the response is 409, the preview no longer matches current billing, allowed billing modes, or capacity state; refresh the plan configuration and ask the user to confirm the updated billing impact before retrying.

Stripe Checkout is not used to add another project to an existing workspace subscription. Existing subscriptions are updated server-side after the user has confirmed the returned billing impact. If the environment disables server-confirmed creation, plans that would require updating an existing subscription are reported as unavailable.

Engine API

The engine exposes four operational endpoints under /api: /api/clients, /api/tunnels, /api/sse, and /api/websocket. The list endpoints answer point-in-time inventory queries, while the signaling endpoints send a filtered initial snapshot followed by incremental updates.

In hosted mode, the engine remains scoped to a single project. In self-hosted mode, this may be the only HTTP API surface present.

The machine-readable engine OpenAPI contract is published by the website at /api/engine/openapi.json and by the engine itself at /api/openapi.json when that engine build includes discovery. The API catalog advertises the website copy so agents can discover the Engine API schema before they know a project-specific engine host.

Authentication

Control plane routes use standard bearer authentication through Authorization. Engine list endpoints (/api/clients and /api/tunnels) also require Authorization: Bearer <token>.

Engine signaling endpoints (/api/sse and /api/websocket) accept Authorization: Bearer <token> and also accept rstream.token=<token> in the query string for browser clients that cannot attach headers reliably. The query-token form is accepted only on those two signaling endpoints.

When rstream.token is used on /api/sse or /api/websocket, the engine requires a short-lived watch token:

  • token type must be auth or app; personal access tokens are rejected;
  • token must have an expiration and the remaining TTL must be greater than zero and no more than one hour plus one minute;
  • token resources must be watch-only: tunnel listing is allowed, while tunnel creation and tunnel connection resources are not allowed;
  • if no token-level tunnel resource is present, equivalent read-only resource permissions are allowed only when tunnel and stream mutation permissions are absent.

Browser integrations should mint that query token at runtime through a backend route. If that route can be authenticated by more than one mechanism, make the intended credential explicit; for example, when using a delegated bearer token, send Authorization: Bearer <token> and omit ambient browser cookies.

For backend code, SDK-managed authentication remains the preferred path, especially when application credentials, fine-grained tokens, project-endpoint resolution, or managed TURN issuance are involved.

Agent automation permissions

Agents that operate tunnels usually need permissions on both surfaces. Control plane API permissions let the agent discover or manage projects. Engine API permissions let the agent create tunnels, dial private tunnels, and inspect live runtime state.

OpenAPI operations expose backend checks through x-rstream-required-rules. The same OpenAPI document exposes x-rstream-permissions, a dictionary mapping each OAuth permission to the rules it allows, and x-rstream-use-case-permissions, a small set of practical bundles for common agent workflows. Agents should use the bundles for normal product tasks and fall back to the rule dictionary when planning a custom workflow.

Common Control plane API permissions:

TaskPermission
List, select, or resolve tunnel projectsaccount.projects.read-only
Read project creation optionsaccount.plan.read-only
Create, update, transfer, or delete tunnel projectsaccount.projects.read-write
Read hosted request and connection logsnetwork.streams.read-only
Read hosted project plan, usage, or TURN usageaccount.plan.read-only
Create managed TURN credentialsturn.credentials.create
Mint short-lived delegated tokensaccount.tokens.create
Manage long-lived credentialsaccount.credentials.read-write

Common Engine API permissions:

TaskPermission
Read active clients, active tunnels, and live eventstunnels.resources.read-only
Create or close tunnels through the CLI, SDK, or enginetunnels.tunnels.create-delete
Dial private tunnels or open and close tunnel streamstunnels.streams.create-delete

Common TURN runtime permissions:

TaskPermission
Allocate TURN relay sessionsturn.relay.allocate

For an agent that must create or select a hosted project, open a local tunnel, follow live state, and read hosted logs, a practical OAuth scope request is:

account.projects.read-write account.projects.read-only account.plan.read-only network.streams.read-only tunnels.resources.read-only tunnels.tunnels.create-delete tunnels.streams.create-delete

For an agent that only needs to use an existing project and open a tunnel next to a local service, prefer:

account.projects.read-only tunnels.resources.read-only tunnels.tunnels.create-delete

For an agent that only connects to private tunnels, prefer:

account.projects.read-only tunnels.resources.read-only tunnels.streams.create-delete

Only request token or credential-management permissions when the user explicitly asks the agent to issue, rotate, revoke, or manage credentials. For tighter delegation inside one project, combine permissions with resources.tunnels boundaries documented in Fine-Grained Tokens.

Engine list parameters

/api/clients and /api/tunnels accept an optional params query parameter encoded as JSON. On both endpoints, limit restricts the number of returned objects and filters narrows the result set. These calls are already scoped to the authenticated project, so filters focus on the resources inside that project rather than on workspace or project metadata.

Tunnel filters are typically built around labels, publish state, protocol, HTTP version, ownership, client attachment, or tunnel name:

curl -G "https://<engine>/api/tunnels" \
  -H "Authorization: Bearer <token>" \
  --data-urlencode 'params={"filters":{"name":"ssh-prod-01","labels":{"service":"ssh","env":"prod"}}}'

Client filters are usually built around agent metadata such as agent, channel, version, os, arch, labels, or user_id:

curl -G "https://<engine>/api/clients" \
  -H "Authorization: Bearer <token>" \
  --data-urlencode 'params={"filters":{"agent":"rstream","channel":"dev"}}'

Labels are usually the most stable tunnel selector in practice. See Labels for the broader model.

Engine signaling parameters

/api/sse and /api/websocket accept the same params query parameter, but the structure separates client filters from tunnel filters. Browser watch connections must put a short-lived token in the rstream.token query parameter because browser WebSocket/EventSource APIs cannot attach arbitrary Authorization headers.

curl -N -G "https://<engine>/api/sse" \
  --data-urlencode 'rstream.token=<token>' \
  --data-urlencode 'params={"clients":{"agent":"rstream"},"tunnels":{"labels":{"service":"ssh","env":"prod"}}}'

The same params value can be used on /api/websocket. In both transports, the initial state.initial event is filtered before it is sent, and subsequent client.* and tunnel.* events are filtered with the same rules. This is the same model exposed by the CLI and SDK watch helpers. For more detail, see Signaling.

TURN issuance

TURN credential issuance belongs to the Control plane API, not to the engine list or signaling API. The raw hosted routes are POST /api/projects/tunnels/<project-id>/turn-server/credentials and POST /api/projects/tunnels/resolve/<project-endpoint>/turn-server/credentials. For production code, the SDK helpers documented in STUN and TURN remain the preferred entrypoint.

Both routes accept an optional JSON body:

{
  "ttlSeconds": 600
}

ttlSeconds must be an integer from 1 to 3600. The TURN runtime rejects usernames whose remaining lifetime is greater than one hour, so callers must not treat this API as a long-lived credential endpoint. The returned username has the shape v1:<exp>:api:<project-endpoint>:<user-id>.

The authenticated user or application credential must have active access to the target project. When stored credential restrictions are present, turn.credentials.create and matching resources.tunnels boundaries are required for managed issuance. Locally derived PAT or APP TURN credentials are checked by the TURN runtime with turn.relay.allocate.

When raw HTTP is the right choice

Raw HTTP is most useful from runtimes where no rstream SDK exists yet, for very small operational probes, or for direct inspection of list and signaling behavior. The SDKs are the better fit when project resolution, application credentials, short-lived or fine-grained tokens, TURN issuance, or one abstraction across hosted and self-hosted modes is required.

For the higher-level surfaces built on top of these routes, see JS SDK, Go SDK, Signaling, Labels, and STUN and TURN.