guides

Build a client (iOS, Android, desktop)

TRaX is built to be driven by your own code — including full third-party "thick clients" that produce a show without ever opening our web studio. A rich client doesn't speak one protocol; it speaks several, one per job. This guide is the map: which transport for which task, and how to authenticate each.

The two authentication models — pick by who you are

Before anything else, decide who your code acts as:

You're building… Authenticate as Credential
A first-party app a user logs into (mobile / desktop that produces or controls a show) the user OAuth 2.0 (PKCE) → user access token (JWT), sent as Authorization: Bearer <jwt>
Server-to-server automation / a third-party integration (dashboards, bots, headless jobs) yourself (the developer) an API key: Authorization: Bearer sk_live_… on the /v1 REST API

These aren't mutually exclusive — a mobile app might log the user in with OAuth for live control and call /v1 with the user's token for reads — but the question "am I logging a user in, or running my own automation?" decides your primary path.

Transport map — which API for which job

I want to… Transport How Auth
Control a studio + receive live state (scenes, sources, go-live, presence, chat control) WebSocket connect to the studio control plane (below) user bearer
Publish camera / mic / screen from the app WebRTC WHIP POST an SDP offer to the studio's WHIP endpoint — see WHIP / WHEP media session token (Bearer)
Publish a hardware / OBS / camera feed SRT or RTMP(S) SRT streamid · RTMP the input's stream key, inside the publish URL
Stream to a live input the app created itself SRT or RTMP(S) POST …/sources then POST …/sources/{sourceId}/ingest for the URL — see Stream to a live input sk_live_ key (sources:write + sources:ingest) to provision, stream key in the URL to publish
Contribute a phone camera into a studio as a participant SRT provision an ingest, then publish — see Contribute from a phone user bearer to provision, scoped stream key in streamid to publish
Watch the program output (confidence monitor) WebRTC WHEP mint a read token + URL, then play — see Monitor the program output media read token (Bearer)
Watch the program or a source preview — now WebRTC WHEP POST an SDP offer to the WHEP endpoint — see WHIP / WHEP media read token (Bearer)
Watch at lowest latency — later MoQ (roadmap) not shipped yet; use WHEP today —
List / read studios, sources, destinations, stream status; go live / offline REST /v1 the Developer API sk_live_ API key (or user bearer)

Rule of thumb for a thick client: one WebSocket to control a studio and stream its live state; WebRTC (WHIP up / WHEP down) for media; REST (/v1) for headless reads and automation. Stream paths (the studios/{id}/… names you publish and play) follow one grammar — see Stream paths.

Native-app authentication — OAuth 2.0 with PKCE

A first-party app logs the user in against the TRaX identity provider using the Authorization Code flow with PKCE (the standard, secret-less flow for native and single-page apps). You get back a user access token (JWT) and a refresh token; the access token is what every TRaX surface accepts as Authorization: Bearer <jwt>.

Identity provider: https://auth.traxstreaming.live

Purpose Endpoint
Authorize https://auth.traxstreaming.live/oauth/v2/authorize
Token https://auth.traxstreaming.live/oauth/v2/token
End session (logout) https://auth.traxstreaming.live/oidc/v1/end_session

Scopes: openid profile email offline_access (offline_access is what returns a refresh token). You'll register your app (to get a client id and your allowed redirect URIs) with the TRaX team — contact us for a native app registration. Registration also puts your client id on the studio control plane's audience allowlist: until it is there, your tokens work on /v1 but the WebSocket and the studio-api mints answer 401 (see the audience note).

The flow:

  1. Generate a PKCE pair. Create a random code_verifier, then code_challenge = BASE64URL(SHA256(code_verifier)).

  2. Open the authorize URL in a system browser / ASWebAuthenticationSession (iOS) / Custom Tab (Android) — not an embedded webview:

    https://auth.traxstreaming.live/oauth/v2/authorize
      ?response_type=code
      &client_id=<your-client-id>
      &redirect_uri=<your-redirect-uri>
      &scope=openid%20profile%20email%20offline_access
      &code_challenge=<challenge>
      &code_challenge_method=S256
    

    Use a custom URI scheme (com.yourapp://callback) or a loopback redirect for the native round-trip.

  3. Handle the redirect — you receive ?code=<authorization_code>.

  4. Exchange the code for tokens (no client secret — PKCE stands in for it):

    curl -sS https://auth.traxstreaming.live/oauth/v2/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d grant_type=authorization_code \
      -d client_id=<your-client-id> \
      -d code=<authorization_code> \
      -d redirect_uri=<your-redirect-uri> \
      -d code_verifier=<code_verifier>
    

    The response includes access_token (a JWT), refresh_token, and expires_in. Store them in the platform keystore (iOS Keychain / Android Keystore), and use grant_type=refresh_token with the same client_id to get a fresh access token before it expires. Access tokens last 1 hour and refresh tokens 30 days unused (90 days at most); read expires_in rather than assuming either. See Token lifetimes.

Send the resulting access token as Authorization: Bearer <jwt> on the WebSocket upgrade, on WebRTC WHIP/WHEP signaling, and on /v1 REST calls.

This is the same OAuth-PKCE pattern this docs site itself uses to log in — a native app differs only in the redirect (custom scheme / loopback instead of a web /callback page) and in storing tokens in the OS keystore.

Native vs. browser: the header advantage

A native app can set request headers everywhere, which keeps auth uniform:

  • WebSocket — set Authorization: Bearer <jwt> on the upgrade request. (Browsers can't set headers on a WebSocket handshake, so the web client has to fall back to a single-use ?ticket= — a native app has no such limit and should use the header.)
  • WebRTC WHIP / WHEP — set Authorization: Bearer <token> on the signaling POST.
  • REST /v1 — Authorization: Bearer … as usual.

Preferring the header on native keeps the token out of URLs and logs.

Controlling a studio (WebSocket)

Real-time studio control — scenes, sources, going live, presence, chat control, and the live state stream back to your UI — runs over a WebSocket to the studio control plane:

wss://studio-api.traxstreaming.live/api/v1/ws/studios/{studioId}/events

Open it with the user's bearer token in the Authorization header. Where a header is impossible (the browser WebSocket API), first POST https://studio-api.traxstreaming.live/api/v1/ws-ticket with the header and the body {"path": "/api/v1/ws/studios/{studioId}/events"}, then connect with ?ticket=<ticket> from the reply. A ticket is single-use, bound to that one path, and expires after 60 seconds (expiresInMs), so mint it right before connecting. Do not put the JWT itself in the URL: the legacy ?token=<jwt> fallback is still accepted for old clients but is being retired. The server resolves the user's access to that studio once at upgrade, then gates each operation by role. This socket is version-gated — add a ?client=<epoch-seconds> build stamp to the connect URL (or send it as an X-Trax-Client header). Frames are JSON — request/reply RPCs plus server-pushed realtime events.

For a concrete walkthrough — connecting, seeding the source list + layout, and the RPCs that add / move / activate sources and switch presets and scenes — see Drive the studio canvas.

The full RPC + event catalog for this surface is first-party/internal today. If you're a logged-in first-party developer, see the internal reference for the exhaustive method and event lists (WS RPCs, WS events, and the private transport map). Third-party apps reach the platform through the public /v1 REST gateway and this WebSocket — never internal service endpoints directly.

Where to go next

  1. Developer API (/v1) — REST reference, endpoint table, API keys, and OpenAPI client generation.
  2. Authentication & session tokens — token claims and how to verify a TRaX-issued token against our JWKS.
  3. WHIP / WHEP — publish and play media over WebRTC.
  4. SRT streamid · RTMP / RTMPS — push a hardware or OBS feed.
  5. Contribute from a phone — join a studio as a camera source over SRT, with reconnection and QR walk-up guests. Stream to a live input — the /v1 path for an app that creates its own input and publishes to it with an API key.
  6. Drive the studio canvas — control layout, sources, presets, and scenes over the studio WebSocket.
  7. Monitor the program output — play the studio's program feed over WHEP as a confidence monitor.
  8. Stream paths — the studios/{id}/… path grammar shared by every media transport.

Tell us what you're building — the roadmap follows what the community wants to ship.