Why You Need This
If you’re building a web app, you have a problem:connect() needs an API key, but anything you ship to the browser is public.
Bundle it in your JS, and anyone can open DevTools and grab it.
Client credentials fix this by splitting the connection into two steps:
- Your server uses the API key to mint a short-lived token.
- Your client connects with that token. No API key required.
Quick Start
Step 1: Mint credentials on your server
| Field | Type | Description |
|---|---|---|
session_id | string | Unique session identifier, derived from the JWT |
signaling_url | string | WebSocket URL for the signaling server |
session_token | string | Short-lived JWT for session authentication |
expires_in | number | Token lifetime in seconds |
capabilities | object | undefined | Streamer capabilities (e.g. { image_to_video: true }). Present when the assigned streamer supports image-to-video. (v1.3.0+) |
Step 2: Connect from the browser
Cross-SDK Serialization
Both SDKs produce the same JSON format. You can mint credentials on a Python backend and consume them in JavaScript on the frontend (or the other way around).The
session_id in the dict is informational.
On deserialization, it gets re-derived from the JWT to prevent tampering.Session Lifecycle and Token Expiration
When you callcreateClientCredentials(), Odyssey provisions a session and returns a session_token (a JWT) that’s valid for expires_in seconds (currently 600s).
Your client needs to call connectWithCredentials() before that window closes.
Once the client is connected and streaming, the token expiration doesn’t interrupt an active stream.
The connection stays alive as long as the WebRTC session is open.
Token expiration only matters for the initial connection.
Here’s what happens when a session ends:
- Client disconnects cleanly: The session is dropped immediately and your concurrency slot is freed.
- Client loses connection (crash, network drop): The session is dropped once the token expires.
- Client never connects: Unused sessions are automatically cleaned up after 15 minutes of idle time.
If a client tries to connect after the token has expired, the connection will be rejected.
The unused session is cleaned up automatically, but your concurrency slot may stay occupied for a few minutes until that happens.
createClientCredentials() call provisions a new session.
You can’t reuse a credential for a different session.
There’s no token refresh. If a token expires before the client connects, mint a new one from your server.
Error Handling
Client Credentials vs Broadcast
Both patterns let end users watch Odyssey streams without an API key. The difference is whether each user gets their own world or everyone shares one.| Client Credentials | Broadcast | |
|---|---|---|
| Sessions | One per user | One shared by all viewers |
| Token | session_token (full control) | spectator_token (view only) |
| User interaction | Yes: startStream(), interact(), endStream() | View only (you orchestrate input at the app level) |
| GPU cost | One GPU per user | One GPU total |
| Good for | Interactive web apps | Live demos, watch parties, Twitch-style streams |
spectator_token and connectToStream().
Full Example
A minimal Express server with a vanilla HTML frontend:SDK Reference
JavaScript SDK
createClientCredentials() and connectWithCredentials() reference.Python SDK
create_client_credentials() and connect_with_credentials() reference.