# Calling from the browser

A softphone is a person's own phone line inside the browser: a `channel_routes` row with `channel='softphone'`, a per-person SIP endpoint, and a dialler whose every state comes from a SIP event rather than from a click. Dialling does not hand the carrier a number to ring — the carrier posts back to the same answer URL a customer's call arrives on, marked `Direction=outbound`, and the XML decides who rings.

- **Status:** Available
- **Audience:** both
- **Channels:** softphone
- **In the app:** #/calls
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/phone/softphone/

## A person's line is a route like any other

`channel_routes` already answers "whose endpoint is this and what is it for" for email, WhatsApp and the business number, so a browser line is a row in it with `channel='softphone'`. That is not a filing decision; it is what lets the same permission, hours and routing logic apply to a person's line without a second implementation of any of it.

Each person gets their own SIP endpoint. The password is generated rather than chosen, stored encrypted, served only to its owner and only by POST, and can be revoked individually — one person losing a laptop is one credential, not a shared line everybody has to re-register.

> **Careful** The browser registers over SIP-to-WebSocket, and the Content Security Policy has to name that WebSocket address explicitly. If it does not, registration fails silently: the dialler looks idle rather than broken, and there is no error anywhere a user would look.

## The address that answers

The registrar address comes from the provider's own browser SDK. Their written documentation names a different one, which answers nothing from any network — every browser registration failed while both the code and the CSP pointed at the documented address.

```text
wss://registrar.vobiz.ai:5063   # the address the provider's browser SDK uses
# the address in the provider's written documentation does not answer
```

The lesson generalises past this one carrier: when a browser transport silently never connects, verify the endpoint against what the vendor's own client library uses at runtime, not against what their reference page says it should be.

## Every state comes from a SIP event

The dialler never shows a state because somebody clicked something. Ringing, answered, held, ended — each is rendered from the SIP event that says so. A click that produces no event produces no state change, which looks unresponsive for a moment and is honest: the alternative is a dialler that says "connected" while the far end is silent.

That rule is why a registration problem shows up as a line that never becomes ready, rather than as a call that appears to place itself and then does nothing.

## How a browser call actually rings somebody

1. The dialler sends a SIP INVITE to the endpoint's registrar with the number in it.
   - Result: The carrier does **not** dial that number. This is the step most people assume and it is wrong.
2. The carrier posts to the voice application's answer URL — the same URL a customer's inbound call arrives on — carrying `Direction=outbound`.
   - Result: One handler now has two completely different jobs, told apart only by that field.
3. `carrier_incoming` matches the `From` value against the workspace's `channel='softphone'` routes.
   - Result: The call is attributed to the person whose endpoint placed it, inside the owning workspace.
4. `may_place_call` is asked under the business's own line.
   - Result: Memory blocks, consent, autonomy and budget apply to a person's browser call exactly as they apply to one Connect places itself.
5. The answer is `<Dial callerId=…>` — or a spoken refusal, if the gate said no.
   - Result: Only now does a phone ring anywhere. The bridged leg is written as a `Call` with `direction='outbound'` and `owner_kind='human'`, so the record distinguishes a person's call from the voice's.

An endpoint that is not attached to the voice application registers perfectly and can never place a call. The registration is with the registrar; the dialling is done by the application. Both have to be true, and only one of them is visible in the dialler.

## The defect this design produced

`Direction` was, for a while, never read. Because the carrier posts to the same answer URL either way, a person dialling out from the browser was greeted as though they were a customer calling in — and the number they had dialled never rang at all. Nothing errored; the greeting played into the agent's own headset.

Reading the carrier's own call log next to the `calls` table is the fastest audit available for this class of fault. A completed call on their side with no matching row on yours is a bug every time, and it is the only way some of these show up.

## Questions

### Does a browser call go through the same rules as an automated one?

Yes. `may_place_call` runs under the business's line for both, so a suppressed number, a memory block, an autonomy rule or a spent budget stops a person's browser call the same way it stops one the voice would have placed. The refusal is spoken rather than silent.

### Why does my line register but every call fail?

The endpoint is almost certainly not attached to the voice application. Registration is a conversation with the registrar and succeeds on its own; placing a call depends on the application answering the carrier's request with dialling instructions.

### Can two people share one softphone endpoint?

They should not. Each endpoint is a `channel_routes` row with its own generated credential, and call attribution — including `owner_kind='human'` on the resulting record — is done by matching the endpoint. Sharing one makes every call on it belong to the wrong person.

## Related

- [Placing outbound calls](https://connectbyjbrh.com/docs/phone/outbound-calls/)
- [How a call reaches your workspace](https://connectbyjbrh.com/docs/phone/carrier-routing/)
- [The call record](https://connectbyjbrh.com/docs/phone/call-record/)
- [Configuring a phone line](https://connectbyjbrh.com/docs/phone/line-configuration/)
- [Consent on calls](https://connectbyjbrh.com/docs/phone/call-consent/)
- [Designing telephony that does not name its provider](https://connectbyjbrh.com/research/provider-independent-telephony/)

## What this page is based on

- Connect phone source pack — `docs-source/sources/PHONE.md` §11, the softphone
- Connect phone source pack — `docs-source/sources/PHONE.md` §2, the six production defects
- Connect capability registry — `docs-source/facts.py` (`softphone`, `CHANNELS`)
