Concepts / Credentials & TTL

Credentials & TTL

Every call to POST /credentials/ice creates a new, single-purpose TURN credential. This page explains its lifecycle and how to choose a TTL.

What a credential is#

A credential is a TURN username and password, bundled with the STUN and TURN URLs in WebRTC's iceServers format:

  • username: a random UUID, unique per request
  • credential: a random 32-character hex password
  • urls: stun:stun.turnix.io:3478, plus four TURN URLs on the selected region's host <region>.turnix.io
  • expiresAt: the moment the credential stops being accepted, in ISO 8601 UTC

Turnix stores the credential in your project together with the region, the request's country, and any client and room labels. You can browse them under Credentials in the dashboard.

The TTL#

ttl is the credential's lifetime in seconds. Turnix sets expiresAt = now + ttl.

Format Whole seconds, digits only (3600, not 3600.0 or "1h"). Anything else returns 422 {"error":"TTL must be an integer"}.
Default When you omit ttl, the platform default applies: 30 seconds.
Bounds The API does not enforce a minimum or maximum. A ttl of 0 creates a credential that has already expired.
The default is very short

30 seconds is only enough if the client uses the credentials immediately. If there is any delay between fetching credentials and creating the peer connection (a lobby, a permission prompt, a slow signaling round trip), set ttl explicitly.

When expiry matters#

The TURN server checks expiresAt when a client authenticates to create a relay allocation. That happens when ICE gathering starts, and again on every ICE restart, because a restart creates new allocations.

A relay that was set up before expiresAt is not cut off when the credential expires; the call continues. What fails after expiry is setting up new relays with the same credential, for example a renegotiation or an ICE restart after a network change.

Choosing a TTL#

Scenario Suggested ttl
Credentials fetched right before new RTCPeerConnection() 300–600
Typical calls, credentials fetched when the user joins 3600
Long sessions where you cannot refresh before an ICE restart Up to the longest expected session, for example 14400

Shorter is safer: a leaked credential can relay traffic, billed to you, until it expires. Longer is more forgiving of delays. Most apps are well served by 3600 plus a refresh before ICE restarts.

Refreshing credentials#

  • New connection: request new credentials for every new RTCPeerConnection. Requests are cheap and each one gets its own username.
  • ICE restart: if expiresAt has passed or is close, fetch new credentials first and update the connection:
javascript
async function restartIceWithFreshCredentials(pc) {
  const { iceServers } = await fetch("/api/turn-credentials").then((r) => r.json());
  pc.setConfiguration({ ...pc.getConfiguration(), iceServers });
  pc.restartIce();
}
  • Do not cache credentials in localStorage or reuse them across users.

Security checklist#

  • Request credentials from your backend only. The API token must never reach a browser or app.
  • Protect your credentials endpoint with your app's own authentication, so strangers cannot use it to get free TURN relay.
  • Prefer short TTLs and refresh instead of long-lived credentials.
  • If software you do not control needs TURN access (Nextcloud Talk, Jitsi), use shared-secret credentials instead of pasting a long-lived credential into its config.

Last updated October 30, 2026