Browser Calling with WebRTC
How browser calling works on Sautikit: your server mints a token valid 60 to 300 seconds, the @sautikit/webrtc SDK registers, and calls bill in KES.
How browser calling works on Sautikit: your server mints a token valid 60 to 300 seconds, the @sautikit/webrtc SDK registers, and calls bill in KES.
Sautikit supports calling from the browser. Your server mints a short-lived token with POST /v1/webrtc/token, the browser registers with it through the @sautikit/webrtc SDK, and every call is billed to your workspace's KES wallet. Tokens are short-lived, typically 60 to 300 seconds, and are only needed to register.
Your API key must never be exposed in a browser. Your backend mints a short-lived token and hands it to the signed-in browser session. The token lets that session register and place calls for your workspace without revealing the API key.
// Server side only
const resp = await fetch("https://api.sautikit.com/v1/webrtc/token", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SAUTIKIT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ phone_number: "+254720000000" }),
});
const session = await resp.json();curl -X POST "https://api.sautikit.com/v1/webrtc/token" \
-H "Authorization: Bearer $SAUTIKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone_number": "+254720000000"}'{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"endpoint": "wss://...",
"protocol": "...",
"clientName": "...",
"turnServer": { "iceServers": [] }
}The body must be valid JSON; send {} at minimum. phone_number (or tenant_number_id) binds the session to one of your numbers. Tokens are short-lived, typically 60 to 300 seconds, and are only needed to register.
Mint the token on demand and return the whole response to signed-in sessions only. Each user and each tab gets its own token.
// Express example (server side only)
app.post("/api/webrtc-token", requireAuth, async (req, res) => {
const resp = await fetch("https://api.sautikit.com/v1/webrtc/token", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SAUTIKIT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ phone_number: "+254720000000" }),
});
res.set("Cache-Control", "no-store");
res.json(await resp.json());
});@sautikit/webrtc handles the WebSocket connection and reconnection, registration, microphone access, WebRTC negotiation and call events.
import { Client } from "@sautikit/webrtc";
// session is the JSON your server returned from the token mint
const client = new Client({
token: session.token,
endpoint: session.endpoint,
protocol: session.protocol,
turnServer: session.turnServer,
});
client.on("registered", () => client.call("+254720000001"));
client.on("answered", () => console.log("connected"));
client.on("hangup", () => console.log("call ended"));Incoming calls arrive as client.on("incoming", ({ from }) => ...); answer with client.accept().
The token is tied to your workspace, so every call placed through the browser is attributed to it. The call is charged to your KES wallet when it ends, at the same rate as a call placed through the API. The call record appears in GET /v1/calls with direction: "outbound".