batondocs← SiteSign in
API reference/Credentials

Get ICE servers / credentials

The one endpoint you need. Authenticate with your project API key, get back a short-lived credential pair plus a ready-to-use multi-transport iceServers array.

Endpoint

POSThttps://api.usebaton.io/v1/credentials

Call this server-side, authenticated with the project API key. Forward only the result to the browser.

Request

Header / fieldTypeDescription
AuthorizationheaderBearer <YOUR_API_KEY> — the project key from your dashboard.
Content-Typeheaderapplication/json
ttlnumberLifetime of the returned credential in seconds. Defaults to 3600 (1 hour). Mint a fresh pair before it expires.
regionstringWhich region the relay should run in — one of the slugs in Regions below. Must be enabled on your project.

Regions

Pass one of these slugs as region. The response echoes back the region the relay was allocated in.

SlugLocation
us-eastUnited States — East
us-westUnited States — West
eu-centralEuropean Union — Frankfurt
eu-westEuropean Union — Ireland
uk-londonUnited Kingdom — London
in-mumbaiIndia — Mumbai
ap-singaporeSingapore
ap-sydneyAustralia — Sydney

Need a region that isn't listed? Ask us — we add regions on request.

curl -X POST https://api.usebaton.io/v1/credentials \
  -H "Authorization: Bearer $BATON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ttl": 3600, "region": "eu-central" }'

Response

You get back one credential pair, reused across all transport URLs. The client's ICE agent tries them in parallel and keeps whichever connects first — you don't pick a transport.

200 OK · application/json
{
  "username": "1718200000:project_ab12",
  "credential": "<ephemeral-credential>",
  "ttl": 3600,
  "region": "eu-central",
  "iceServers": [
    {
      "urls": [
        "stun:relay.usebaton.io:3478",
        "turn:relay.usebaton.io:3478?transport=udp",
        "turn:relay.usebaton.io:3478?transport=tcp",
        "turns:relay.usebaton.io:443?transport=tcp"
      ],
      "username": "1718200000:project_ab12",
      "credential": "<ephemeral-credential>"
    }
  ]
}

Response fields

FieldDescription
username<unix-expiry>:<project> — the credential is valid until the Unix timestamp encoded in the prefix.
credentialThe ephemeral credential. Pair it with username.
ttlSeconds the credential remains valid from issue.
regionThe region the relay was allocated in.
iceServersDrop-in array for RTCPeerConnection. Same username/credential on every URL.

Why all four URLs are returned

URLRole
stun:…:3478Gather server-reflexive candidates; connect directly when possible — no relay.
turn:…:3478?transport=udpPreferred relay path — lowest latency, best quality.
turn:…:3478?transport=tcpFallback when UDP is blocked.
turns:…:443?transport=tcpTURN over TLS on 443 — looks like HTTPS, traverses strict DPI / deny-all corporate and hospital firewalls. The endpoint that matters most for enterprise clients.

Credential model

Baton issues a short-lived ephemeral token — an opaque username/credential pair. Your server fetches it from the credentials API with your project API key; you don't derive or compute it. The username encodes the token's expiry (<unix-expiry>:<project>), and the credential is the matching secret for that single token.

  • One model, no exceptions. A long-lived API key (server-side) is exchanged for a short-lived ephemeral token. The client only ever sees the token. There is no shared secret and no static credential.
  • Mint per session, server-side. Call the endpoint when you need a token and hand only the result to the browser. The API key never reaches the client.
  • TTL & expiry. The token stops working at the Unix time in its username. Request a fresh one before then — for long calls, refresh ahead of expiry.
  • Clock skew. The relay validates the expiry against its own clock. Keep your servers on NTP; if a token is rejected as expired right after issue, suspect a skewed client/server clock.

Use the credentials on the client

// iceServers came from YOUR server (which called Baton)
const pc = new RTCPeerConnection({
  iceServers,
  iceTransportPolicy: "all"   // use "relay" to force-test the relay path
});