Overview
Business-initiated calls (BIC) let your business place outbound voice calls to WhatsApp users. The user must have granted you call permission first — see Call Permissions. Permissions can be temporary (7 calendar days) or permanent.
BIC is not available when the business phone number's country is United States, Canada, Egypt, Vietnam, or Nigeria. The recipient can be in any country where Cloud API is available.
Prerequisites
- Subscribe your app to the
callswebhook field. - Calling enabled on the business phone number (see Calling Configuration).
- Approved call permission from the recipient — error
138006if absent. - Valid payment method on the WABA. UIC is free, but BIC is per-pulse billed (see Pricing).
- Business phone number country in the BIC-supported list.
The Four-Step BIC Flow
- Obtain permission. Send a call permission request (free-form or template) and wait for the user to accept, or rely on
callback_permission_status: ENABLEDto auto-grant when the user calls you. See Call Permissions. - POST
/callswithaction: connectand an SDP offer from your WebRTC stack. - Apply the SDP answer Meta returns via the Call Connect webhook to your WebRTC stack to establish the media session. You'll then receive
RINGING,ACCEPTED, orREJECTEDstatus webhooks. - Terminate when done — either you POST
action: terminate, or the user hangs up. Either way, a Call Terminate webhook arrives.
The ACCEPTED status webhook typically arrives after the call is already established — Cloud API sends it primarily for auditing, not as a precondition for sending media.
Initiate a Call (POST /calls)
POST /<PHONE_NUMBER_ID>/calls
{
"messaging_product": "whatsapp",
"to": "12015550123",
"action": "connect",
"session": {
"sdp_type": "offer",
"sdp": "<<RFC 8866 SDP>>"
},
"biz_opaque_callback_data": "0fS5cePMok"
}
| Field | Notes |
|---|---|
to | Recipient WhatsApp user phone number |
action | connect for new calls. Other values: pre_accept, accept, reject, terminate (used for user-initiated calls) |
session.sdp_type | offer for BIC |
session.sdp | RFC 8866 SDP — OPUS only, 48 kHz media clock, 8 kHz DTMF clock, ptime 20ms, single SSRC. See API Reference → Media Path Guidelines |
biz_opaque_callback_data | Optional. Up to 512 chars. Echoed back in subsequent webhooks for tracking |
Success response — note the wacid. prefix (WhatsApp Call ID, distinct from wamid. for messages):
{
"messaging_product": "whatsapp",
"calls": [
{ "id": "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA" }
]
}
Rate limit: 10,000 new calls per 24 hours per business phone number.
biz_opaque_callback_data
An arbitrary string (up to 512 chars) that you pass into the call and that comes back unmodified in subsequent webhooks. Cloud API doesn't process it — it's purely for your own correlation (tying a call to a CRM record, an outbound campaign, etc.).
It's available on:
- The Call Connect webhook (BIC)
- The Call Terminate webhook (BIC and UIC if you accepted with one)
- The Call Status webhook for
RINGING/ACCEPTED/REJECTED
Set it on POST /calls with action: connect (BIC) or action: accept (UIC). It can't be added later.
Terminate a Call
POST /<PHONE_NUMBER_ID>/calls
{
"messaging_product": "whatsapp",
"call_id": "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA",
"action": "terminate"
}
Always call terminate even if you sent an RTCP BYE in the media path — this ensures accurate billing. You don't need to call it when the user hangs up; you'll just receive the Call Terminate webhook.
Success response:
{ "success": true }
Call Connect Webhook (SDP Answer)
Arrives near-real-time after a successful POST /calls connect. Apply the SDP answer to your WebRTC stack to start media flow.
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "calls",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "15550001234",
"phone_number_id": "123456789012345"
},
"calls": [
{
"id": "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA",
"to": "12015550123",
"from": "15550001234",
"event": "connect",
"direction": "BUSINESS_INITIATED",
"timestamp": "1749196895",
"biz_opaque_callback_data": "0fS5cePMok",
"session": {
"sdp_type": "answer",
"sdp": "<<RFC 8866 SDP>>"
}
}
]
}
}
]
}
]
}
Call Status Webhook (RINGING / ACCEPTED / REJECTED)
Mirrors the messaging-status webhook envelope but with type: "call":
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "calls",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "15550001234",
"phone_number_id": "123456789012345"
},
"statuses": [
{
"id": "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA",
"type": "call",
"status": "ACCEPTED",
"timestamp": "1749197000",
"recipient_id": "12015550123",
"biz_opaque_callback_data": "0fS5cePMok"
}
]
}
}
]
}
]
}
status is one of RINGING, ACCEPTED, REJECTED. A REJECTED status is followed by a Call Terminate webhook. ACCEPTED typically arrives after media is already flowing.
Call Terminate Webhook
Arrives whenever the call ends — user hangs up, you call action: terminate or action: reject, network failure, etc.
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "calls",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "15550001234",
"phone_number_id": "123456789012345"
},
"calls": [
{
"id": "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA",
"to": "12015550123",
"from": "15550001234",
"event": "terminate",
"direction": "BUSINESS_INITIATED",
"timestamp": "1749197480",
"status": "COMPLETED",
"start_time": "1749196900",
"end_time": "1749197480",
"duration": 580,
"biz_opaque_callback_data": "0fS5cePMok"
}
],
"errors": [
{ "code": 138019, "message": "Call setup failed" }
]
}
}
]
}
]
}
| Field | Notes |
|---|---|
status | COMPLETED (call was answered) or FAILED |
start_time, end_time, duration | Only present when the other party picked up. duration is in seconds. |
errors | Present only on failed calls — see Troubleshooting → Calling Error Codes |
Limits
| Limit | Default |
|---|---|
| New BIC calls per business phone number | 10,000 / 24h |
| Connected BIC calls per business + user pair | 100 / 24h (sandbox: relaxed) |
| Connected BIC calls per business + user pair (per pricing tier) | 5 / 24h on the lowest-volume tier |
| Concurrent inbound calls per phone number | 1,000 |
| Consecutive unanswered BIC calls before "reconsider" system message | 2 (sandbox: 5) |
| Consecutive unanswered BIC calls before permission auto-revoked | 4 (sandbox: 10) |
A connected call resets unanswered counters and call-permission-request rate limits between that business and that user.
Common Errors
| Code | Meaning | Fix |
|---|---|---|
138000 | Calling not enabled | Configure call settings (see Calling Configuration) |
138001 | Receiver uncallable (not on WhatsApp, hasn't accepted ToS, or unsupported client) | Confirm with recipient |
138002 | Concurrent calls limit (1,000) reached | Throttle inbound capacity |
138003 | Duplicate call — one is already ongoing with the receiver | Wait for current call to end |
138005 | Call rate limit exceeded | Reduce sending rate |
138006 | No approved call permission found | Send permission request first (Call Permissions) |
138012 | BIC daily limit hit (100 connected calls / 24h) | Wait for next-call timestamp in error_data |
138013 | BIC not available for this business phone number country | Check availability list |
138014 | Calling temporarily disabled (low quality) | Improve outreach quality, wait for restriction expiration |
138017 | Permission request can't be sent — permanent permission already exists | Just place the call directly |
Full error reference and SIP-specific errors are on Calling Troubleshooting.