Route Inbound Calls to a Browser Client
Ring one specific browser agent on an inbound call with a dial to client:<name>, and why that name must match the client's live registration.
Outbound calling from the browser is the common first step: your server mints a token, the browser SDK registers, and the agent dials out. Routing an inbound call back to a specific browser agent is the other half, and it has one rule that trips up almost everyone: you ring a browser client by its registered name, and that name must match exactly.
When a call comes in, your voice callback returns a dial to
client:<name>. The platform hands that identity to the PBX, which bridges the
caller to wherever that client is currently registered. If <name> is not a
client that is registered and online at that instant, the call does not ring at
all — it fails in about a second and is recorded as a missed call.
@sautikit/webrtc that
registers and can place outbound calls. See
Browser calling with WebRTC.client:<name> is an internal identity, not a phone number. You do not build
a SIP URI for it (sip:… or user/..@realm will be rejected) — you pass the
bare client:<name> and let the platform resolve it to the live registration.
The <name> you dial must be the exact clientName the browser client is
registered under:
clientName returned by POST /v1/webrtc/token — the same value you
set as client_name when you mint the token, if you set one.Mint server-side with your bearer key and set client_name to the identity you
intend to dial. Give each agent a stable, distinct name so you can ring one
agent and not the whole floor.
// Your backend — POST /v1/webrtc/token with your bearer API key.
const res = 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({
// The identity you will dial as client:<name>. Keep it stable per agent.
client_name: "hostnali-agent",
}),
});
const session = await res.json();
// { token, endpoint, protocol, turnServer, clientName, sipProfile, expiresIn }
// session.clientName is the name to dial — "hostnali-agent" here.Hand the whole token object to the browser SDK. The client registers under the
name from the token; once you see registered, it is reachable by
client:<that name>. Answer inbound calls with the incoming event.
import { Client } from "@sautikit/webrtc";
const client = new Client({
token: session.token,
endpoint: session.endpoint,
protocol: session.protocol,
turnServer: session.turnServer,
});
client.on("registered", () => console.log("online as", session.clientName));
client.on("incoming", async ({ from }) => {
if (confirm(`Incoming call from ${from}. Answer?`)) {
await client.accept();
} else {
client.reject("busy");
}
});On an inbound call, return a dial whose number is client:<name>, using the
same name the agent is registered under. You can greet the caller first and
record the connected leg as usual.
import { sauti } from "@sautikit/node";
res.json({
actions: [
sauti.say({ text: "Thank you for calling Hostnali..." }),
sauti.dial({
number: "client:hostnali-agent",
callerId: "+254709221592",
record: "record-from-answer",
}),
],
});{
"actions": [
{ "say": { "text": "Thank you for calling Hostnali..." } },
{
"dial": {
"number": "client:hostnali-agent",
"callerId": "+254709221592",
"record": "record-from-answer"
}
}
]
}<Dial callerId="+254709221592" timeout="30" record="record-from-answer"><Number>client:hostnali-agent</Number></Dial>The caller hears the greeting, then the browser agent rings. On answer the two legs are bridged and recording starts.
Because a client is addressed by its registered name, per-agent routing is just a matter of naming:
client_name when you mint their token
(for example their user id or extension), and dial that specific name.numbers and first_answer_wins:{
"actions": [
{
"dial": {
"numbers": ["client:agent-101", "client:agent-102"],
"callerId": "+254709221592",
"first_answer_wins": true,
"record": "record-from-answer"
}
}
]
}A name in the list that is not currently registered is simply skipped; only online agents ring.
online as <name> on the
registered event. If it never fires, the client is not reachable yet.Dialing and the answer — not Dialing and Missed in the same
second. An instant miss means the dialled name had no live registration.clientName from the token.dial parameters