Test your browser calling setup with the SDK demo
The dashboard ships a built-in test-call page, Developer → SDK Live Demo, at/developer/sdk-demo. It places a real outbound call from
the browser through LiveKit/WebRTC — the same path the dashboard
softphone and the Softphone component in the Web SDK
use — so you can verify your whole browser-calling chain from one
screen before you embed it in your own app.
This page walks the demo end to end: what you need before you start,
what the page does at each step, how to read success, how to read
every common failure, and where to go next.
1. Prerequisites
Check these three things before you open the page — most demo failures trace back to one of them. Role. Placing a call spends wallet credit, so the page is restricted to the owner, admin, and developer roles. Agents, viewers, and billing roles can’t reach it. If the page doesn’t appear in your Developer menu, ask an org owner to grant you one of those roles. Wallet. The call bills per minute against your organization’s wallet. An empty wallet stops the dial at the guard with anINSUFFICIENT_BALANCE error, so top up first — a sandbox test call
still needs a positive balance, even if the per-minute charge rounds
to cents.
LiveKit token basics. A LiveKit token is a short-lived,
signed credential that authorizes your browser to join your
organization’s voice room and publish audio into it. Every browser
softphone on Orbit — the dashboard dialer, the Web SDK Softphone
component, and this demo — gets one before it can send or receive a
single audio frame. The demo fetches it for you; when you embed the
softphone yourself you’ll fetch it the same way, through
POST /voice/softphone/token.
You also need a modern browser on HTTPS (the dashboard always is) and
permission to grant it microphone access.
2. Open the demo page
In the dashboard, open the Developer section in the sidebar and choose SDK Live Demo. The route is/developer/sdk-demo.
The page is a single card: an optional Caller ID field, a required
Destination field, a Call button, and a status badge
(Idle before you dial). Once a call is live you also get a state
readout, a mute toggle, and a dial pad for DTMF.
3. The token step
When you press Call, the page runs the same two-step sequence every browser softphone uses:- Get a LiveKit token —
POST /voice/softphone/tokenreturns the short-lived token. The browser uses it to join your organization’s LiveKit voice room and publish your microphone track into the room. - Dial the PSTN leg —
POST /voice/softphone/dialbridges the room to the destination number over the wholesale softswitch.
Token request and response
Fetch the token yourself the same way when you embed the softphone outside the dashboard:token— the LiveKit access token. It’s a JWT signed by Orbit’s media control plane with a one-hour TTL (DEFAULT_TOKEN_TTL_SECONDS= 3600 inorbit-media-manager). The browser hands it to LiveKit as the join credential.room_name— the deterministic room the user will join (softphone:<tenant_id>:<user_id>). Reconnects land in the same room, so a refresh mid-call doesn’t strand state.media_url— the WebSocket URL the browser connects to. This is the one field you need from the response for the client-side connect.livekit_url— a deprecated alias ofmedia_url. Usemedia_url; the alias will be removed in a future minor version.
- 401 — you sent no recognizable credential (no session cookie, no API key), or the session/key has been revoked. Sign back in or re-key.
- 403 — the credential is valid but
INSUFFICIENT_PERMISSIONS(your role isn’t owner/admin/developer, or your API key lacksvoice:write). Have an org owner raise the role or mint a voice-scoped key.
Joining with the token
The returned token is the second argument LiveKit’sRoom.connect()
expects. Point the client at media_url from the previous response
(never at a hardcoded hostname):
Softphone.fromToken(token, ...) call wraps the token fetch and
refresh, and reuses this same POST /voice/softphone/token endpoint
under the hood. Use the component rather than hand-rolling the LiveKit
join whenever you can; this manual flow is what you fall back to when
you need the raw LiveKit client.
4. Place the call
- Caller ID (optional): enter an E.164 number your org owns (for
example
+14155552671). Leave it blank and the API picks your first dialable number. Ownership is enforced either way — the caller ID gate is an ownership check, not a format check. - Destination: enter the destination in E.164 format (for example
+15559876543). Use a sandbox number or your own phone — not a customer. - Press Call and allow the microphone prompt.
- Watch the state badge move through
connecting→dialing→ringing→on_call.
5. Interpret success
A healthy first call looks like this:- The badge climbs
connecting→dialing→ringing→on_callwithin a few seconds. - You hear ringback, then the far side’s audio, in the browser tab.
- The far side hears your microphone. Toggle the mute button; the button flips to a muted state and the far side goes silent.
- The dial pad enables once the media leg connects (before that it is disabled with a hint). Press a digit and the far side hears the DTMF tone — the standard way to walk an IVR.
- Hang up from the page. The badge returns to
Call ended.
6. Interpret failures
A failed call lands on a red Failed badge, a short toast headline, and a specific inline reason in the card. Match the signature to the cause:
About that SIP-WSS signature. The Web SDK also exposes a raw
SIP-over-WSS register path for teams that want classic SIP semantics.
Registering a raw SIP client requires a provisioned SIP WSS gateway,
and in most environments that gateway is not provisioned — the
register is rejected and the client reports a voice-gateway connection
failure. The dashboard softphone and this demo deliberately avoid that
path and ride LiveKit/WebRTC instead, which works out of the box. If
your own embedded client fails at register time with that signature,
the fix is architectural, not a retry: move to the LiveKit-based
Softphone component.
7. Next steps to embed
When the demo passes, move to the browser softphone setup guide. It covers device registration, softphone credentials, inbound routing, and embedding the softphone in your own app. The hand-off is deliberately small: the two-step sequence — fetch a LiveKit token, then dial the PSTN leg — is identical between the demo and your embedded client. The Web SDKSoftphone component wraps
those exact two calls; the demo page merely exposes them as a manual
form. What the demo does not cover, and the setup guide adds, is
credential management for your agents, inbound call routing to the
softphone, and the optional SIP-over-WSS register path for raw SIP
clients.
8. FAQ
Who pays for demo calls? Your organization’s wallet. Every demo call bills per minute exactly like a production outbound call, which is why the page warns against dialing customers and why the wallet check is a prerequisite. Why is the page role-gated? Because it spends money. Owner, admin, and developer roles can commit wallet credit; agents, viewers, and billing roles cannot, so the page excludes them. If you need a teammate to run the demo, an org owner grants them one of the three allowed roles. Should I embed via LiveKit or raw SIP-over-WSS? LiveKit. The LiveKit path is the one the dashboard and this demo prove end to end and the one that works without extra provisioning. The raw SIP-WSS path exists for teams that specifically need standard SIP register and call-control semantics, and it depends on a SIP WSS gateway being provisioned for your environment — until it is, register fails. Start with theSoftphone component; revisit SIP only if you have a hard
SIP-semantics requirement.