Two-way reachability
A signal2sip-managed number is a full member of both worlds - it can be called from either side, and calls placed on either side reach the other.
Signal call in → PBX
When someone calls a signal2sip account on Signal, the daemon answers only once the PBX side actually picks up (it doesn’t steal the call from your other real/linked Signal devices before that), then bridges the audio to whichever destination that account is configured to dial:
sip_bridge_destination- dial a specific extension directly.sip_bridge_did- dial a DID and let the PBX’s own Inbound Route decide the real destination (ring group, IVR, time conditions, failover) - useful if you want that logic to live in your PBX config instead of signal2sip’s.
PBX call out → Signal
Dial through a signal2sip account’s own SIP extension like any other outbound call, and it places a real Signal call to whatever number you dialed. The destination is resolved through Signal’s Contact Discovery Service the same way a real Signal client would, so no manual mapping between phone numbers and Signal identities is needed.
DTMF (dialing digits into an IVR)
Signal’s own apps have no in-call dialpad at all, so signal2sip uses the
same pattern already proven in this project’s sibling bridge for
Telegram (tg2sip-webrtc),
which hits the identical limitation there: while a call with someone is
up, send digits as a plain text message in that same conversation - it
gets picked up and relayed as a real SIP DTMF event (RFC 2833) into the
bridged call, without needing any in-call UI Signal doesn’t have. Any
combination of 0-9, *, #, and A-D (case-insensitive) works, up to
32 characters per message.
Hangup propagates both ways
Ending the call on either side - Signal or the PBX - ends it on the other side too, instead of leaving a dangling half-connected call on one leg.
Connection health: Signal is the source of truth, not the PBX
The dependency between the two sides is deliberately one-directional:
- If the Signal connection drops, the daemon immediately takes that account’s SIP registration down too - an honest “Unregistered” beats a stale “Registered” that nothing can actually route to. It keeps retrying the Signal connection in the background, and restores the SIP registration automatically the moment it reconnects.
- If the SIP/PBX side becomes unreachable (PBX restart, network blip), the daemon does not touch the Signal connection - the account stays online and reachable on Signal regardless, while a separate, independent watchdog keeps retrying SIP registration on its own until the PBX is reachable again.
The asymmetry is intentional: a dead Signal connection means the account can’t do anything useful at all (no call in either direction is possible without it), so tying SIP’s state to it avoids ever presenting a falsely-healthy registration. A dead PBX, on the other hand, is just one account’s outbound leg being temporarily unavailable - it shouldn’t take down an otherwise-healthy Signal connection that may still be handling messages or other accounts’ calls.