User-initiated call
A WhatsApp user calls the business number. Meta sends a TLS SIP INVITE to Asterisk. Asterisk authenticates and identifies Meta, then sends the normalized destination into a VICIdial DID and inbound group.
Meta can deliver a WhatsApp call to Asterisk. VICIdial still needs a secure trust boundary, a real DID route, and proof that signaling, media, agent delivery, and reporting all survive end to end.

A WhatsApp SIP connection looks like another carrier until the first real call exposes the differences. The business server's public certificate decides whether Meta delivers an inbound INVITE. Digest authentication and a maintained source ACL decide whether Asterisk should trust it. Number normalization decides whether VICIdial finds the DID. ACK routing and SRTP decide whether a connected call stays connected.
This guide works through those boundaries in order. It is written for VICIdial administrators and telephony engineers with shell access to Asterisk and administrator access to VICIdial. It is not a beginner SIP tutorial.
A WhatsApp user calls the business number. Meta sends a TLS SIP INVITE to Asterisk. Asterisk authenticates and identifies Meta, then sends the normalized destination into a VICIdial DID and inbound group.
VICIdial selects a permitted destination. Asterisk sends an INVITE to wa.meta.vc, receives a 407 challenge, and retries with the Meta-generated SIP credential.
There are two boundaries. Meta knows SIP and WhatsApp identity; it does not know the generated contexts, DIDs, campaigns, queues, or reports inside VICIdial. Asterisk must bridge those worlds without turning a public TLS listener into an open trust boundary.
can_receive_call_sip status.Verify the certificate before debugging dialplan logic:
openssl s_client -quiet \
-verify_hostname voice.example.com \
-connect voice.example.com:5061
The following is an illustrative, self-contained adaptation of Meta's Asterisk example. It makes inherited endpoint behavior visible and separates inbound authentication from outbound authentication. Confirm every option against the Asterisk version actually installed.[2]
[transport-whatsapp-tls]
type=transport
protocol=tls
bind=0.0.0.0:5061
cert_file=<PUBLIC_CERTIFICATE_CHAIN>
priv_key_file=<PRIVATE_KEY>
ca_list_file=<TRUSTED_CA_BUNDLE>
verify_server=yes
allow_wildcard_certs=yes
external_media_address=<PUBLIC_IP>
external_signaling_address=<PUBLIC_IP>
local_net=<PRIVATE_CIDR>
[sdes_endpointtemplate](!)
type=endpoint
transport=transport-whatsapp-tls
context=from-whatsapp
acl=meta_whatsapp_sip
disallow=all
allow=opus,ulaw,alaw
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=no
media_use_received_transport=yes
media_encryption=sdes
[whatsapp](sdes_endpointtemplate)
auth=whatsapp-inbound-auth
aors=whatsapp
from_user=<BUSINESS_NUMBER_WITHOUT_PLUS>
from_domain=voice.example.com
outbound_auth=whatsapp-outbound-auth
[whatsapp]
type=aor
contact=sip:wa.meta.vc;transport=tls
[whatsapp-identify]
type=identify
endpoint=whatsapp
match_header=X-FB-External-Domain: wa.meta.vc
[whatsapp-inbound-auth]
type=auth
; userpass is broadly compatible with older deployments; current Asterisk may
; internally convert it to digest. Verify the supported auth_type locally.
auth_type=userpass
username=<BUSINESS_NUMBER_WITHOUT_PLUS>
password=<META_SIP_PASSWORD>
[whatsapp-outbound-auth]
type=auth
auth_type=digest
username=<BUSINESS_NUMBER_WITHOUT_PLUS>
password=<META_SIP_PASSWORD>
realm=*
rewrite_contact=no matters because Meta documents a failure where modified Record-Route headers prevent ACK or BYE from following the dialog. The call can answer and carry audio, then fail around 32 seconds.[2]
First inspect a working inbound carrier and the generated dialplan. Do not paste a plausible context name from an article.
asterisk -rx 'dialplan show' | less
asterisk -rx 'dialplan show <EXISTING_VICIDIAL_INBOUND_CONTEXT>'
Then keep the custom boundary narrow:
[from-whatsapp]
exten => _+<BUSINESS_NUMBER_WITHOUT_PLUS>,1,NoOp(Inbound WhatsApp call)
same => n,Goto(<EXISTING_VICIDIAL_INBOUND_CONTEXT>,<VICIDIAL_DID>,1)
same => n,Hangup()
The DID format must exactly match the value configured in VICIdial. Normalize once at this boundary. Confirm the route preserves VICIdial call logging, queue delivery, disposition, and recording behavior.
Meta's route does not use conventional SIP REGISTER. Asterisk sends the destination to wa.meta.vc and answers Meta's digest challenge.[1] Therefore, pjsip show registrations is not a health test for this route.
[from-vicidial-whatsapp]
exten => _+X.,1,NoOp(Permission-gated outbound WhatsApp call)
same => n,Dial(PJSIP/whatsapp/sip:${EXTEN}@wa.meta.vc;transport=tls)
same => n,Hangup()
Use a dedicated local carrier prefix only after checking the existing dialplan for collisions. The actual VICIdial carrier string and campaign assignment are local design decisions. Do not connect this route to a campaign unless the application queries Meta's current permission immediately before dialing, requires start_call.can_perform_action, and matches the returned WhatsApp identity to the normalized destination.
A reload is a mutation, not a validation command. Before touching a running dialer:
module show like pjsip, module show like srtp, and core show codecs.asterisk -rx 'pjsip show transports'
asterisk -rx 'pjsip show endpoint whatsapp'
asterisk -rx 'dialplan show from-whatsapp'
asterisk -rx 'core show channels concise'
Check: DNS, TLS listener, public certificate chain, hostname match, Meta SIP settings, and firewall.
Check: Meta-generated password, fully normalized business-number username, From domain, and auth association.
Check: endpoint identification, inbound digest, ACL, destination format, generated context, DID, and inbound group.
Check: SDP, SRTP mode, Opus availability, NAT, advertised media address, and RTP firewall. Meta expects the business side to send the first SRTP packet for both directions.[1]
Check: ACK and BYE routes. Preserve Meta's Record-Route headers and inspect rewrite_contact=no.[2]
Check: whether the custom route passed through VICIdial's normal logging boundary, used the intended lead, and completed disposition and recording policy.
A loaded endpoint is not proof. Treat each row below as an acceptance gate.
| Boundary | Pass condition | Evidence to retain |
|---|---|---|
| Eligibility | Calling, SIP, geography, identity match, unexpired permission, and start_call.can_perform_action pass immediately before dialing | Redacted settings and runtime permission decision |
| TLS | Meta validates the business server's public hostname and certificate chain; Asterisk validates wa.meta.vc on outbound TLS | Certificate verification results for each direction |
| Inbound trust | Digest, endpoint identification, and maintained Meta ACL all pass | Redacted SIP challenge and ACL match |
| Outbound auth | Initial INVITE receives 407; authenticated retry is accepted | Redacted SIP dialog |
| Media | Two-way audio remains stable beyond the ACK timeout window | RTP/SRTP statistics and human confirmation |
| VICIdial | Expected DID, inbound group, agent, lead, disposition, and recording policy are used | Redacted VICIdial records |
| Hangup | Both sides clear normally when either side ends the call | BYE and channel cleanup |
| Regression | Existing carriers, campaigns, agents, and inbound groups still work | Pre/post smoke results |
Those values are the implementation. Replacing them with plausible examples would make this article easier to copy and less safe to use.
No. Meta provides SIP signaling for WhatsApp Business Calling. Asterisk must terminate that signaling securely, then map calls into VICIdial routing, queues, reporting, and recording.
Yes. A working SIP route is not permission to call. Before every business-initiated call, validate current Meta calling permission and confirm that it belongs to the same normalized WhatsApp identity as the destination.
Not for the Meta route shown here. Meta challenges a business-initiated INVITE with digest authentication and sends user-initiated INVITEs to the configured TLS server. A registered status for another carrier does not prove this route works.
A common cause is broken ACK or BYE routing after Record-Route headers are changed. Meta documents preserving its route headers and using rewrite_contact=no for this Asterisk failure mode.
No. The Meta-facing PJSIP objects are a self-contained pattern, but certificate paths, Asterisk capabilities, generated include files, ACL names, VICIdial contexts, DIDs, and carrier strings must be discovered and tested on the target installation.
We can review compatibility, routing, permission enforcement, security controls, acceptance evidence, and rollback before a production change.