> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser Softphone — place and receive calls from the dashboard

> The dashboard's built-in phone: a full calling surface for owners, admins, and developers inside the dashboard — token to dial, inbound rings, dispositions, caller ID picking, and where it differs from the Web SDK Softphone.

# Browser Softphone — place and receive calls from the dashboard

The Browser Softphone is the dashboard's built-in phone. It places and
receives real calls from a browser tab, over WebRTC, without a desk phone,
a SIP app, or a browser extension. It is the same underlying path the
floating dialer and the SDK-demo test page ride: a short-lived LiveKit
token joins a per-user voice room, then a dial request bridges the room
to the PSTN through jambonz and the Devotel softswitch — the org's
wholesale outbound path. This page is not an external SIP client; it's
the in-dashboard surface.

## 1. What it is

The Browser Softphone page lives under **Voice → Softphone**.
`POST /voice/softphone/token` issues a short-lived credential for the
current user's deterministic room (`softphone:<tenant>:<user>`), and
`POST /voice/softphone/dial` starts the PSTN leg. Outbound termination
exits through jambonz → the Devotel softswitch, exactly like every other
production voice path. There is **no SIP REGISTER step** on this surface
— the browser joins over the signed-in dashboard session and listens for
inbound frames. That is also why the surface deliberately does **not**
use the Web SDK `Softphone` class (the raw SIP-over-WSS register path)
described on the [Web SDK](/sdks/web) page: that class depends on a
SIP WSS gateway that is not provisioned in most environments, so its
register call is rejected. The dashboard uses the LiveKit path instead,
which works out of the box.

The floating softphone shell on every dashboard route, the global
IncomingCallDialog, and the Developer → SDK Live Demo page all ride the
same `useSoftphone()` state machine, so this page stays in lockstep with
the softphone experience everywhere else.

## 2. Role gate

Placing a call spends wallet credit and registers a callable identity,
so only **owner**, **admin**, and **developer** roles reach the surface.
Viewers, billing, and agent roles are excluded — same gate as the
Developer SDK-demo page. If a teammate can't see the page, an org owner
must change their role; it is the same gate the Developer SDK-demo page
carries.

## 3. Inbound and outbound flows

At rest the page shows a **Ready** badge when the real-time event stream
is connected (the LiveKit equivalent of "registered presence"), or a
"Connecting to Orbit…" hint when it is not, since inbound rings arrive
only when that stream is live.

**Outbound.** Pick a caller ID from the organization's active numbers
(the picker lists every owned, active number), type the destination in
E.164 form (for example `+15559876543`), and press **Call**. The dial
runs through the same `POST /voice/softphone/token` →
`POST /voice/softphone/dial` sequence the floating dialer uses, and the
page watches the badge climb `Connecting…` → `Dialing…` → `Ringing…` →
**On call**.

**Inbound.** A ringing `Incoming call` banner appears with **Answer** /
**Decline** buttons, tracking the shared `IncomingCallDialog` so two
dashboard tabs never double-answer. Accepting a call flips the badge to
**On call**.

**Hang up.** **Hang up** terminates the call; the badge falls back to
**Call ended**. Every call placed or accepted through this page is a
standard one — it lands in the Voice call logs with its caller/callee,
duration, recording, and the disposition tag the responsible party set,
so agent QA and analytics see the same trail the floating dialer
leaves.

## 4. Caller ID and number sources

The picker fetches your organization's active numbers and defaults to
your saved preferred caller ID (the same persisted setting the floating
dialer and incoming-call reply share). An extension that provisions a
direct number can deep-link here with `?from=<E.164>` to adopt that
number as the shared caller ID when it's owned and active. If no owned
number can dial (all suspended, releasing, or still activating), the
page says so with the specific reason, instead of a generic "No active
numbers" state — pick a caller ID the ownership gate accepts, since the
dial itself rejects a non-owned number.

## 5. Limits

* **Scope.** Token and dial endpoints accept `voice:read` /
  `voice:write` scope; the page gates to owner/admin/developer.
* **Per-second dial caps.** The voice surface is rate-limited like
  other read/write endpoints (read budget / write budget under
  `RATE_LIMIT_AUTH_READ` / `RATE_LIMIT_AUTH_WRITE`); an idle softphone
  never dials and so never exhausts the write budget.
* **Concurrent sessions.** The softphone room is scoped to the current
  user, and two tabs opened on the same session share the same
  `useSoftphone()` state, not two parallel call sessions.

## 6. When to use this versus your own SIP client

Use the Browser Softphone when the work is **yours** — a sales call, an
escalation, a supervisor barge, a demo — and when the call start or
accept should leave the same dashboard that hosts the rest of the
workflow. Use your own SIP client (a desk phone, a Zoiper-like app, the
Web SDK `Softphone` embedded in your own app) when the agent works from
a fixed PBX seat, the organization also needs registrations on desk
phones, or you need classic SIP register semantics — the
[registering-your-pbx-on-orbit](/guides/registering-your-pbx-on-orbit)
guide covers the PBX trunk side, and the [Web SDK Softphone](/sdks/web)
describes embedding the softphone class in your own application. The
dashboard softphone does **not** use the SDK Softphone class — it rides
the LiveKit room directly, and it never SIP-REGISTERs.

## 7. Escape hatch — token + dial from the API

`POST /voice/softphone/token` (any `owner/admin/developer` credential; an
empty body is accepted):

```bash theme={null}
curl -X POST 'https://api.orbit.devotel.io/api/v1/voice/softphone/token' \
  -H 'X-API-Key: dv_live_sk_your_key_here'
```

Response (abridged):

```json theme={null}
{
  "data": {
    "token": "<livekit access token>",
    "room_name": "softphone:org_2x8demo:user_9a1f23",
    "media_url": "wss://media.orbit.devotel.io"
  }
}
```

Then `POST /voice/softphone/dial` with the destination and (optional)
caller ID:

```bash theme={null}
curl -X POST 'https://api.orbit.devotel.io/api/v1/voice/softphone/dial' \
  -H 'X-API-Key: dv_live_sk_your_key_here' \
  -H 'Content-Type: application/json' \
  -d '{ "to": "+15559876543", "from": "+14155552671" }'
```

Both endpoints are the same pair the dashboard's `useSoftphone()`
context calls, so the live call flows under the same brand and billing
account as the page. If something hangs in the page you can re-create
the exact call sequence against the API and rule the surface in (or out).

## See also

* [Voice extensions (SIP credentials)](/voice/extensions) — provision a
  direct-dial extension so the `?from=` deep-link adopts the right
  caller ID.
* [Registering your PBX on Orbit](/guides/registering-your-pbx-on-orbit)
  — the SIP trunk side, when a fixed seat and SIP register matter.
* [Set up the browser (WebRTC) softphone](/guides/voice-softphone-browser-calling)
  — full inbound-routing, credential, and client wiring for the
  same surface on an embedded build.
* [Web SDK Softphone](/sdks/web) — the embeddable SIP-over-WSS class
  when you need SIP register semantics in your own application.
* [SDK demo test-call page](/guides/sdk-demo-browser-softphone) —
  place a real outbound test call from Developer → SDK Live Demo.
