Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt

Use this file to discover all available pages before exploring further.

How WebRTC Signaling Works

WebRTC itself has no signaling protocol — it only defines how to establish media. The signaling (how you say “call this number” or “I’m ringing”) is up to the application. Here’s how the Telnyx WebRTC SDK does it.

The Signaling Path

Key components:
ComponentRoleProtocol
Your AppUser interfaceJavaScript
TelnyxRTC SDKSDK logicInternal
VSPVoice SDK Proxy — translates WebSocket ↔ SIPWebSocket + SIP
SIP ProxyRoutes SIP messages to carriersSIP/UDP
B2BUA-RTCMedia gateway (WebRTC ↔ RTP)DTLS/SRTP + RTP
VSP handles signaling only. B2BUA-RTC handles media only. They are separate systems.

WebSocket Connection

The SDK opens a single persistent WebSocket to rtc.telnyx.com:
const client = new TelnyxRTC({ login_token: jwt });
client.connect();

// WebSocket lifecycle:
// 1. DNS resolves rtc.telnyx.com → nearest VSP instance
// 2. TLS handshake (port 443)
// 3. WebSocket upgrade
// 4. Login message sent (JWT or credential)
// 5. Server responds with session info
// 6. telnyx.ready fires — you can make/receive calls

What the DNS resolution does

rtc.telnyx.com resolves to the nearest VSP based on DNS-based geo-routing:
RegionVSP DC
North AmericaNJ1
EuropeAMS3, FR5
Asia-PacificCN1
If the DNS routes to a suboptimal VSP (e.g., an Indian client hitting FR5 instead of CN1), call latency increases. See Configure Network & Firewall for troubleshooting.

Outbound Call Flow

When you call client.newCall(): What the SDK does at each step:
  1. newCall() — Creates a Call object, starts ICE gathering
  2. SIP INVITE — SDK sends invite message over WebSocket, VSP translates to SIP
  3. SDP negotiation — Codec selection (OPUS, PCMU, PCMA), ICE candidates exchanged
  4. Ringing — Remote party’s phone is ringing. call.state === 'ringing'
  5. Answer (200 OK) — Remote party picked up. call.state === 'active'
  6. Media flows — Audio transmitted via WebRTC (separate from signaling)

Inbound Call Flow

When someone calls your WebRTC client: What the SDK does:
  1. Incoming INVITE — VSP receives SIP INVITE, pushes to SDK over WebSocket
  2. callUpdate notificationnotification.call.state === 'ringing'
  3. Your app decides — Call call.answer() or call.hangup()
  4. Answer — SDK sends 200 OK, establishes WebRTC media
  5. Media flows — Two-way audio established

Session Description Protocol (SDP)

During call setup, both sides exchange SDP (Session Description Protocol) to agree on:
SDP NegotiatesExample
Audio codecsOPUS (preferred), PCMU (G.711u), PCMA (G.711a)
Codec parametersOPUS with FEC, stereo/mono, sample rate
ICE candidatesHow to reach each other for media
DTLS fingerprintFor encrypting media (SRTP)
Media directionSendrecv (two-way), sendonly, recvonly
BandwidthMaximum bitrate
The SDK handles SDP negotiation automatically. You don’t need to construct SDP manually.

Codec Priority

The SDK’s default codec priority:
  1. OPUS — Best quality, handles packet loss well, variable bitrate
  2. PCMU — G.711μ-law, universal compatibility, 64kbps
  3. PCMA — G.711A-law, European PSTN standard, 64kbps
OPUS is strongly preferred — it handles jitter and packet loss better than G.711, and uses less bandwidth.

WebSocket Reconnection

If the WebSocket drops, the SDK automatically reconnects: See Handle Reconnection for the full reconnection behavior and how to handle it in your app.

Custom Headers

You can pass custom SIP headers in both directions:

Outbound (your app → carrier)

const call = client.newCall({
  destinationNumber: '+12345678900',
  customHeaders: [
    { name: 'X-My-Header', value: 'value1' },
    { name: 'X-Another', value: 'value2' },
  ],
});
These appear as SIP headers in the INVITE.

Inbound (carrier → your app)

Inbound custom headers are available in the notification:
client.on('telnyx.notification', (notification) => {
  if (notification.type === 'callUpdate' && notification.call.state === 'ringing') {
    const customHeaders = notification.call.customHeaders;
    // e.g., { 'X-Caller-Name': 'John' }
  }
});

What Signals What

ActionSDK MethodSIP MessageWho Initiates
Make a callnewCall()INVITEClient
Answer a callcall.answer()200 OKClient
Reject a callcall.hangup()487 Request TerminatedClient
End a callcall.hangup()BYEEither side
Put on holdcall.hold()re-INVITE with sendonlyClient
Resume from holdcall.unhold()re-INVITE with sendrecvClient
Send DTMFcall.sendDigits()INFO (RFC 2833)Client
Mute audiocall.mute()(local only, no SIP)Client
Mute is a local operation — it stops sending audio from your microphone but doesn’t send any SIP signal. The remote party doesn’t know you’re muted (unless you tell them via your app).

See Also