Overview
Request TURN credentials from your server, hand them to your client. Two endpoints, no SDK required.
Base URL
Authentication
Pass your API key as key in the JSON request body. Keys are per-app and available from the dashboard. Never send your API key from client-side code; request credentials from your own backend and forward them to the client.
Endpoints
| Endpoint | Purpose |
|---|---|
| POST /v1/credentials | Get TURN credentials for a user session. Supports regular credentials and gateway-pinned sessions. |
| POST /v1/exchange | Lightweight signaling store. Save up to 10KB of string data in a namespace for 10 minutes to be picked up by another peer. |
Relay Selection
When you request credentials, Tosses geolocates the host_ip (or your server's IP if omitted) and routes to the closest healthy relay based on network proximity and real-time load. If multiple relays tie on location, the least-loaded one wins. Relay health is tracked continuously; overloaded or offline relays are excluded automatically.
IPs are never stored or logged, and are used only for routing purposes.
Quickstart
From zero to working TURN credentials in a few minutes.
1. Get an API Key
Sign up at the dashboard. Your first 1 GB is free with no card required. Create an app and copy the API key (it's only shown once).
2. Request Credentials (Server-Side)
3. Build the ICE Server Config
The response is already a valid ICE server object — pass it directly to the client:
Credential Lifetime
Credentials expire after expiry_ttl seconds (default 600). Expiry only prevents new allocations; active relay sessions aren't terminated when credentials expire. For long-lived sessions, refresh credentials before expiry and call pc.setConfiguration + pc.restartIce().
Credentials
Request TURN credentials for a single user session.
Request Body
| Field | Type | Description |
|---|---|---|
| key required | string | Your app's API key. |
| host_ip optional | string | The host peer's public IP address. Used for relay selection. Defaults to the IP the request came from. |
| expiry_ttl optional | string or number | Credential lifetime in seconds. Default is 600. Accepts an integer (1800) or a string ("1800"). Pass "Gateway" to create a gateway instead of regular credentials. Must be positive. |
| gateway optional | string | A gateway ID returned from a previous call with expiry_ttl: "Gateway". Returns 180-second credentials pinned to that gateway's relay. |
Example Request
Response: Regular Credentials
Gateways
A gateway is a persistent relay reservation. Every user who gets credentials through the same gateway is routed to the same relay, even if their credentials are requested at different times.
When to Use a Gateway
For multiplayer rooms or any session where multiple users need to share the same relay. Instead of generating one long-lived credential for the room (which expires), you generate a gateway once and hand each user their own short-lived credential from it.
Gateways never expire. Balance is checked each time a credential is issued through them, not upfront for the whole session.
Step 1: Create a Gateway
Response:
Store the gateway ID server-side for the lifetime of your room. It encodes the relay assignment and your app ID.
Step 2: Issue Credentials per User
Each time a user joins, call credentials with the gateway ID. You get back 3-minute credentials routed to the same relay as all other members of that gateway.
Response:
Relay Pinning Logic
When a gateway credential is requested, Tosses looks for any active allocations using that gateway. If found, it routes to that relay regardless of the original relay ID. If not, it picks the best available relay for the host IP, the same way a regular credential does.
This means even if the first user disconnects and a new one joins, as long as any session is active on the gateway's relay, that relay stays pinned.
Exchange
A small signaling store. Write up to 10KB of string data into a namespace and have another peer pick it up; no accounts, no auth, no infrastructure. Data lives for 10 minutes then disappears.
Request Body
| Field | Type | Description |
|---|---|---|
| namespace required | string | A shared key both peers know — typically a room ID, session token, or invite code. |
| data optional | string | The string to add (max 10KB). Adds alongside existing entries (does not overwrite). Omit to read without writing. |
Response
Returns all entries in the namespace keyed as data_0, data_1, etc. Each entry is a separate string added by a different write. The namespace expires 10 minutes after the last write. Rate limited to 8 requests / 10 seconds.
Example: WebRTC signaling
Peer A writes its SDP offer; peer B polls until it appears, then writes its answer to a separate namespace.
Other use cases
Exchange is just a short-lived key-value store, you can use it for anything that needs two parties to hand off a small piece of data without a server in between:
- Passing a relay credential or invite token to a peer joining a room
- Sharing a temporary public key before establishing an encrypted channel
- Coordinating a rendezvous point (IP + port) for a direct UDP connection
- Sending a one-time config blob to a device on first boot
Billing & Credits
Usage is measured in egress GB: what our relays successfully send to the receiving peer. Bytes that never reach the other side aren't billed.
Two-Bucket Balance
Your account has two balance pools:
| Pool | Behavior |
|---|---|
| Subscription | Refills each month per your plan. Used first. Does not roll over. |
| PAYG | Purchased in bundles, never expires. Used automatically when subscription is empty. |
Balance Checks at Credential Time
Before issuing credentials, Tosses estimates how much data the session is likely to consume and checks that your balance can cover it. If your balance is zero or the estimate exceeds your balance, you get a 402. These are forward-looking reserves, not charges — actual billing is based on real egress bytes reported when sessions close.
Estimates use your app's historical averages: throughput (Mbps), allocation length, and allocations per session. Apps with no history use a conservative 2 Mbps default.
For regular credentials: the estimate accounts for all currently active sessions and allocations alongside the new one, scaled by the requested credential TTL.
For gateway credentials: the estimate projects one new allocation at your app's average throughput and duration, on top of however many allocations are already active on that gateway.
Negative Balance
Credential creation is blocked at zero balance. Your balance can go slightly negative if a session uses more than was estimated; you'll need to top up before issuing new credentials. Low-balance email notifications are configurable per account.
Error Reference
All errors return a JSON body with a detail field.
| Status | Meaning | Common causes |
|---|---|---|
| 400 | Bad Request | Missing key, invalid host_ip, non-positive expiry_ttl, malformed gateway ID. |
| 401 | Unauthorized | API key not found or invalid. |
| 402 | Payment Required | Balance too low to cover the estimated session reserve. Top up and retry. |
| 403 | Forbidden | App is paused, app is marked for deletion, or the gateway belongs to a different app. |