# SIP over WebSocket

A browser cannot open the raw sockets ordinary SIP uses, so SIP is carried inside a WebSocket with the `sip` subprotocol. The browser registers with a registrar over that connection, receives and places calls through it, and hands the audio to WebRTC. Connect's softphone works this way: a person's own line is a `channel_routes` row with `channel='softphone'`.

- **Status:** Reference
- **Audience:** developer, both
- **Channels:** phone
- **In the app:** #/calls, #/phone-advanced
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/technology/sip-over-websocket/

## Registration, and what keeps it

Registration is the browser telling a registrar *this account is reachable at this connection*. It is not permanent. Every registration carries an expiry, and the client must renew before it lapses; a laptop that slept through the renewal is not registered when it wakes, regardless of what the screen still says.

1. Open the WebSocket, negotiating the `sip` subprotocol.
   - Result: Both ends agree the frames are SIP. A mismatch here opens a connection that never carries anything, which reads as a hang.
2. Send a REGISTER; answer the authentication challenge that comes back.
   - Result: The registrar knows which account this connection belongs to.
3. Re-register before the expiry, every time.
   - Result: Inbound calls keep finding you. Miss it and calls route to whatever the account's fallback is — often voicemail, sometimes nothing.
4. Accept an inbound INVITE, or send one, and negotiate media.
   - Result: Signalling is settled on this connection; audio flows over the separate WebRTC path.

Because the address a browser can advertise is not reachable from outside, an inbound call has to come back down the connection that is already open. This is why a softphone works behind a network that would refuse to accept an incoming connection — and why a proxy that mishandles that return path produces a line that registers cleanly and never rings.

## The addressing mistakes that cost days

Every one of these fails without an error message, which is what makes them expensive. The connection looks fine; the calls do not happen.

| Mistake | What you see | The correction |
|---|---|---|
| Using the service's web hostname as the SIP host | The socket opens or times out; registration never completes | A hosted platform's SIP host is frequently a different name from the one in its web or API URL. Read it from the platform's SIP settings, never infer it |
| Writing `sip:host:port` where a bare `host:port` is expected | The field is accepted and saved; nothing routes | Match the field's expected form exactly. A scheme prefix in a field that does not want one is not normalised away |
| Assuming a number is linked to a trunk because both exist | Outbound works; inbound never arrives | The association between a number and a trunk is its own step, and on some platforms it exists only in a console rather than in an API |
| Testing inbound by calling from the same platform | A call that appears to work and proves nothing | Originate the test from the outside, over the real path a customer would use |

> **Careful** Registered is not reachable. Registration proves the browser found the registrar; it says nothing about whether a public number routes to the trunk that account sits behind. Those are two separate configurations and they fail independently.

## Does Connect use SIP over WebSocket?

**Used, for the browser softphone.** It is the signalling transport for a person's own line, and it is the only place in the product where a WebSocket carries anything — the application's own event stream is server-sent events.

It is not how the engine's own calls are carried. Connect runs two phone engines: a turn-based carrier path and a realtime speech-to-speech path a workspace opts into, with the PSTN leg handled by a carrier and, on the realtime engine, LiveKit SIP in front of the model. A softphone is a person's handset; those are the business's lines.

## Reading a softphone fault

**The line will not register** — Signalling. Check the SIP host, the credentials and whether the socket is reaching the registrar at all.
**Registers, then drops after a few minutes** — The renewal is not happening, or an intermediary is closing an idle connection. Both look identical from the screen.
**Registers, outbound works, inbound never rings** — Almost never the browser. The number is not routed to the trunk, or the return path through the proxy is broken.
**Connects and is silent** — Not signalling at all — that is the media path, and it has its own causes. Voice line health escalates operational problems into Needs You, where they drain by themselves as the cause clears.

## Questions

### Why does the softphone need a WebSocket when the rest of the app does not?

Because SIP is a two-way conversation with its own message flow, and a browser has no other way to carry it. Product notifications only travel one way, which is why they use an event stream instead. Two different problems, two different transports.

### Can I use a desk phone instead of the browser?

That is a carrier and account question rather than a Connect one — a line that registers over ordinary SIP is between the device and the provider. What Connect models is the route: `channel_routes` with `channel='softphone'` is specifically the browser line.

### Is the signalling connection encrypted?

It runs over a secure WebSocket, so the signalling is protected in transit. The media is encrypted separately and unconditionally by WebRTC. Neither fact tells you anything about the PSTN leg beyond the carrier, which is outside both.

## Related

- [SIP](https://connectbyjbrh.com/docs/technology/sip/)
- [WebSocket](https://connectbyjbrh.com/docs/technology/websocket/)
- [WebRTC](https://connectbyjbrh.com/docs/technology/webrtc/)
- [SIP trunks](https://connectbyjbrh.com/docs/technology/sip-trunk/)
- [Phone and voice in Connect](https://connectbyjbrh.com/docs/phone/)
- [Call routing](https://connectbyjbrh.com/docs/technology/call-routing/)
- [Designing telephony that does not name its provider](https://connectbyjbrh.com/research/provider-independent-telephony/)

## What this page is based on

- Connect capability registry (docs-source/facts.py) — CHANNELS.softphone, CHANNELS.phone, `line_health`
- Connect source pack (docs-source/sources/GENERAL.md §5) — voice line health escalates into Needs You
- https://www.rfc-editor.org/rfc/rfc7118 — The WebSocket Protocol as a Transport for SIP
- https://www.rfc-editor.org/rfc/rfc3261 — SIP: Session Initiation Protocol
