Integrations/Custom WebRTC app
Custom WebRTC app
The canonical flow for any app built on raw RTCPeerConnection: your server mints an ephemeral token from the credentials API, and your client uses it. The API key never leaves your server.
1. Mint a token on your server
Expose a small endpoint that calls Baton with your project API key and returns just the ephemeral result to the browser.
server.js — POST /api/ice
app.post("/api/ice", async (req, res) => {
// BATON_API_KEY lives only here, on the server
const r = await fetch("https://api.usebaton.io/v1/credentials", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.BATON_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ ttl: 3600, region: "eu-central" })
});
const { iceServers } = await r.json();
res.json({ iceServers }); // only the ephemeral token reaches the browser
});2. Use the token on the client
client.js
const { iceServers } = await fetch("/api/ice").then(r => r.json());
const pc = new RTCPeerConnection({
iceServers,
iceTransportPolicy: "all" // use "relay" to force-test the relay path
});One model, everywhere
This is the same flow every integration uses: API key (server) → ephemeral token → client. Fetch a fresh token before the previous one's TTL expires; never embed the API key or any token in client or config files. See Authentication.