How to Connect WhatsApp Business Cloud API For Voice Calling to Asterisk with PJSIP (Vanilla Asterisk) with WebRTC/DTLS/ICE
First release: 14 Aug 2025 – Initial working release
Updated: 16 Aug 2025 – Fixed hangup issue after 30 seconds
Introduction
Meta has officially enabled SIP integration for the WhatsApp Business Cloud API, opening a powerful new channel for customer communication. You can now route WhatsApp voice calls directly to your own Asterisk server.
This guide provides a complete, production-tested configuration for connecting WhatsApp to a “vanilla” Asterisk installation using PJSIP. We’ll focus on creating a stable connection and specifically address common frustrations, like the infamous 30-second call drop, especially for servers on a public IP (e.g., hosted on DigitalOcean, AWS, Vultr, etc.).
Want a quick demo? Add our test number +60327242555 to your contacts and call it from WhatsApp on your mobile. You’ll be connected to a simple IVR for testing purposes.
✅ Prerequisites
Before you start, make sure you have the following ready:
A WhatsApp Business Number with voice calling enabled via the Meta Cloud API.
An Asterisk server with a dedicated public IP address.
A valid, trusted TLS certificate (e.g., from Let’s Encrypt) installed on your server. Meta will reject self-signed certificates.
Your SIP credentials from the Meta for Developers portal:
SIP Username: Your WhatsApp Business Number (e.g., 15551234567).
SIP Password: The unique password generated by Meta.
SIP Server Hostname: The FQDN you provided to Meta (e.g.,
sbc.yourcompany.com
).
Step 1: Configure Global Network Settings (sip.conf
)
Even when using PJSIP for the trunk, you must correctly configure Asterisk’s global SIP settings. This step is crucial for NAT traversal and is the #1 reason calls fail with one-way audio or drop after 30 seconds.
In /etc/asterisk/sip.conf
, add the following under the [general]
section:
; /etc/asterisk/sip.conf
[general]
; This tells Asterisk its public-facing IP address.
externip = YOUR_SERVER_PUBLIC_IP
; This defines the server's local network.
localnet=127.0.0.1/32
Step 2: Create the Secure PJSIP Transport (pjsip.conf
)
Meta requires a secure TLS connection for all SIP traffic. This transport defines the port and certificates Asterisk will use to listen for incoming WhatsApp calls.
Add this to /etc/asterisk/pjsip.conf
:
; /etc/asterisk/pjsip.conf
; ====== TRANSPORT DEFINITION ======
[whatsapp-tls]
type=transport
protocol=tls
bind=0.0.0.0:5061
cert_file=/etc/asterisk/keys/fullchain.pem ; ❗️Update with the correct path to your certificate
priv_key_file=/etc/asterisk/keys/privkey.pem ; ❗️Update with the correct path to your private key
method=tlsv1_2
Note: Ensure the paths to your cert_file
and priv_key_file
are correct.
Step 3: Configure the PJSIP Endpoint (pjsip.conf
)
This is the heart of the configuration. We’ll define the authentication details, media handling, and the critical network settings needed to maintain a stable call with Meta’s infrastructure.
Append the following sections to your /etc/asterisk/pjsip.conf
file. Remember to replace all placeholder values.
; ====== AUTHENTICATION (For Incoming Calls) ======
[YOUR_WHATSAPP_NUMBER-auth]
type=auth
auth_type=userpass
username=YOUR_WHATSAPP_NUMBER ; Use number without the '+' sign
password=YOUR_META_GENERATED_PASSWORD
; ====== ADDRESS OF RECORD (AOR) ======
[YOUR_WHATSAPP_NUMBER]
type=aor
max_contacts=5
; ====== ENDPOINT (The Main Trunk Configuration) ======
[YOUR_WHATSAPP_NUMBER]
type=endpoint
transport=whatsapp-tls
context=from-whatsapp ; The dialplan context for incoming calls
disallow=all
allow=opus ; WhatsApp exclusively uses the Opus codec
aors=YOUR_WHATSAPP_NUMBER
auth=YOUR_WHATSAPP_NUMBER-auth
; --- Critical Media & Connection Settings (Recommended DTLS/WebRTC Setup) ---
; This setup is highly recommended for reliability with Meta's infrastructure.
media_encryption=dtls
dtls_verify=fingerprint
dtls_setup=actpass
webrtc=yes
ice_support=yes ; Essential for negotiating the audio path
rtcp_mux=yes ; Multiplex RTP and RTCP to save ports
; --- THE 30-SECOND CALL DROP FIX (For Public IP Servers) ---
rewrite_contact=no ; ❗️Crucial: Prevents Asterisk from rewriting headers
rtp_symmetric=no ; Works in tandem with rewrite_contact=no
; --- Other Required Settings ---
direct_media=no
trust_id_inbound=yes
send_connected_line=no
identify_by=auth_username
; ====== IDENTIFY (Match Incoming Traffic) ======
; This tells Asterisk to associate traffic from WhatsApp's SBC with the endpoint above.
[YOUR_WHATSAPP_NUMBER]
type=identify
endpoint=YOUR_WHATSAPP_NUMBER
match=wa.meta.vc ; The hostname sent by META (optional)
Why These Settings Matter
allow=opus
: WhatsApp voice calls use the high-quality Opus codec. No other codecs are needed.webrtc=yes
/ice_support=yes
: These enable the modern media negotiation techniques (ICE) that WhatsApp’s backend relies on to establish a stable audio path through firewalls and NAT.rewrite_contact=no
: This is the magic fix for the 30-second call drop. On a server with a public IP, this setting allows Asterisk to correctly honor theRecord-Route
headers from Meta’s proxies. This ensures the finalACK
message is routed correctly, keeping the call alive beyond the 32-second transaction timeout.
Step 4: Create the Inbound Dialplan (extensions.conf
)
Now, we need to tell Asterisk what to do with the call when it arrives. In /etc/asterisk/extensions.conf
, create the context you referenced in the PJSIP endpoint (from-whatsapp
).
; /etc/asterisk/extensions.conf
[from-whatsapp]
; This context handles all calls from the WhatsApp trunk
; Meta sends the caller ID with a '+' prefix, so we match on _+X.
exten => _+X.,1,NoOp(Incoming WhatsApp call from ${CALLERID(num)} to ${EXTEN})
same => n,Log(NOTICE, "Routing WhatsApp call to main inbound dialplan")
same => n,Goto(from-pstn,${EXTEN},1) ; ❗️ IMPORTANT: Change this to your main call-handling context!
same => n,Hangup()
Be sure to change from-pstn
to the actual context where you handle your inbound calls (e.g., an IVR, queue, or ring group).
Step 5: Configure Your Firewall
Your server’s firewall must be configured to accept connections from Meta’s servers.
If you are using UFW on Linux, run the following commands:
# Allow your PJSIP TLS port (defined in Step 2)
sudo ufw allow 5061/tcp
# Allow the standard Asterisk RTP port range for audio
sudo ufw allow 10000:20000/udp
# Reload the firewall to apply changes
sudo ufw reload
Step 6: Reload and Test 🚀
Finally, apply the configuration and watch for your first test call.
From the server’s command line, reload the Asterisk dialplan and PJSIP modules:
Bashasterisk -rx "core reload"
Open the Asterisk verbose console to watch the call in real-time:
Bashasterisk -rvvvvv
Using your personal WhatsApp account, place a voice call to your WhatsApp Business Number. You should see the call details appear in the console and be routed to the dialplan context you specified.
Quick Troubleshooting
Problem: Call drops at exactly 32 seconds.
Solution: This is almost always a NAT or signaling issue where the final
ACK
is lost. Double-check thatexternip
is set correctly insip.conf
and thatrewrite_contact
is set tono
in yourpjsip.conf
endpoint.
Problem: Connection fails with 401/403 Unauthorized errors.
Solution: Verify your
username
andpassword
in the[auth]
section ofpjsip.conf
. The username must be your number without the+
sign.
Problem: No audio or one-way audio.
Solution: This is a firewall or media path issue. Ensure your firewall is allowing the RTP port range (e.g.,
10000-20000/udp
). Also, confirm thatice_support=yes
is enabled in the endpoint configuration.
Problem: Connection Refused or TLS Handshake Errors.
Solution: Check that your TLS certificate is valid, trusted, and not expired. Confirm that the
cert_file
andpriv_key_file
paths in the[transport]
section are 100% correct.
Problem: The first one or two calls fail with “No Answer”.
Solution: This can be caused by latency between your server and Meta’s servers (located primarily in the US). During the initial authentication, Meta may improperly reuse an old security key, causing Asterisk to reject it. Moving your server or a proxy closer to the US/Europe (e.g., New York or London) often resolves this timing-sensitive issue.