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:
Generate a PKCE pair. Create a random
code_verifier, thencode_challenge = BASE64URL(SHA256(code_verifier)).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=S256Use a custom URI scheme (
com.yourapp://callback) or a loopback redirect for the native round-trip.Handle the redirect — you receive
?code=<authorization_code>.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, andexpires_in. Store them in the platform keystore (iOS Keychain / Android Keystore), and usegrant_type=refresh_tokenwith the sameclient_idto get a fresh access token before it expires. Access tokens last 1 hour and refresh tokens 30 days unused (90 days at most); readexpires_inrather 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
/callbackpage) 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 signalingPOST. - 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
- Developer API (
/v1) — REST reference, endpoint table, API keys, and OpenAPI client generation. - Authentication & session tokens — token claims and how to verify a TRaX-issued token against our JWKS.
- WHIP / WHEP — publish and play media over WebRTC.
- SRT streamid · RTMP / RTMPS — push a hardware or OBS feed.
- 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
/v1path for an app that creates its own input and publishes to it with an API key. - Drive the studio canvas — control layout, sources, presets, and scenes over the studio WebSocket.
- Monitor the program output — play the studio's program feed over WHEP as a confidence monitor.
- 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.