Skip to main content

WhatsApp Business Calling media relay

The WhatsApp Business Calling model explains the capability: who may call whom, how permission works, and how calls are billed. This page goes one layer deeper into the question a customer hits when they diagnose “the call connects, but there is no audio”: where does the media go, and what decides whether it gets wired at all? The short answer: signalling and media take different paths. The call is set up over Meta’s Cloud API, while the audio leg is bridged between Meta’s WebRTC (DTLS-SRTP) side and your plain-RTP side by a relay — rtpengine — that the API programs over rtpengine’s NG-control protocol. If the relay host is not configured, everything except the audio still completes: the call record, the billing, the webhooks. That is a signalling-only call, and it is the expected behavior — not a crash — in that configuration. You read this page when you are enabling calling, diagnosing dead audio, or deciding whether the media leg of your deployment is wired at all.

The seven components and the role each plays

Of the seven, four are always on: Meta Cloud API, the calling API pre-flight, webhook fanout, and the wallet check. Two are conditional: the relay pair (host + NG-control endpoint), which is bound by configuration, and recording, which is per-call. The relay pair is the part a customer most often cannot reason about, because “unset” looks identical to “broken” from the outside: the call exists, the webhooks flow, the bill arrives, and there is simply no audio. The rest of this page is about that pair.

Where signalling ends and media begins

The configuration surface is deliberately thin — one variable binds the relay: These are operator values, not tenant settings — mirrored into the API’s environment from the platform’s own secret store. You do not manage them from the dashboard. Signalling-only is the name for the unbound state. It is not an error the call can recover from mid-call; it is a property of the deployment: the API was given no NG endpoint, so no offer/answer ever reached rtpengine, so the RTP legs were never bridged. The call lifecycle is designed to survive that — a relay program failure degrades rather than aborts the call, by design, so provisioning the media gateway cannot break signalling while it is unreachable. Below is the media path as a diagram. The dashed segment on the left is Meta’s WebRTC/DTLS-SRTP side; the solid segment on the right is the plain-RTP side your SIP endpoint terminates on; the box in the middle is the relay that copies packets between them: The bottom two edges are the control plane; the top two are the media plane. A request on the control plane (offer, answer, delete) is what turns the top edges from an intention into a bridged pair of RTP legs.

Inbound: WhatsApp to the SIP endpoint

The inbound direction rings from the WhatsApp client into your number, and the accept step is when the two legs get programmed — marking where signalling hands off to media: The two NG offer / NG answer steps are the entire media story. Everything above them is HTTPS and SIP; everything below them is packet forwarding. If the relay is unbound, the sequence runs unchanged except those two steps are skipped — and the media flows note never becomes real.

Outbound: SIP endpoint to the WhatsApp client

The outbound direction mirrors it, with the wallet pre-flight in front and Meta’s round-trip to originate: Same split: the call cannot start without the wallet and permission pre-flight, the call cannot carry audio without the relay program step, and the call always ends with the metering stamp and the webhook.

Failure semantics per layer

Each layer degrades differently, and the differences are the reason “calls connect but no audio” is a configuration diagnosis rather than a crash report: The design posture is consistent: media-plane failures degrade to signalling-only; signalling-plane and money-plane failures refuse the call outright. A relay outage never produces a half-billed call, and a billing refusal never produces a half-wired relay. For the operator side of wiring the relay, the guide’s media relay section has the exact env-var table and the no-op contract.

How this differs from PSTN voice

If you already run PSTN voice on the platform, the model above has three sharp edges that PSTN does not: The relay handoff is also the reason the optional SDP override field (sdp_offer on the initiate-call request) is an escape hatch, not a mainstream path: it matters only when you control your own audio leg explicitly.

A worked call-receipt trace

This is the shape a reconciling consumer sees — one inbound call, wired, answered, terminated, stamped. The join key on the payload is the wa_call_log_id; the persisted row and every retry of the webhook both carry it, so either side is enough to reconcile:
Read it against the failure table above: a 42-second call with a one-minute floor bills one minute of cost; a call that never wired the relay still produces this payload; a call refused in pre-flight produces none of it. Recording, when consented, appears as its own line so a refund on the recording never disturbs the leg cost. The events preceding the termination are the lifecycle fanout described on the model page: whatsapp.call.received at ring, whatsapp.call.accepted (inbound) or whatsapp.call.connected (outbound), then whatsapp.call.terminated, all signed with the same HMAC scheme as the rest of the platform (Webhooks → Security).

See also