# How to Handle Outbound Calls

In this documentation, you will learn:

* [How to collect **Call Permission Requests (CPR)** using text or template messages](#how-to-collect-cpr)
* [How to **initiate a call** once permission is granted](#how-to-initiate-a-call)
* [Troubleshooting common errors and resolutions](#troubleshooting)

## How to collect CPR&#x20;

Before starting a call you need to collect CPR with either a text or template message. The following steps describe how to do so using a template message:&#x20;

* [#collecting-cpr-with-a-text-message](#collecting-cpr-with-a-text-message "mention")
* [#collect-cpr-using-a-template-message](#collect-cpr-using-a-template-message "mention")

### Collect CPR with a text message&#x20;

You can also use the following request to send a single interactive message to get a CPR from the end-user:

<mark style="color:blue;">`GET`</mark> `https://waba-v2.360dialog.io/calling/permissions/{{CONSUMER-WHATSAPP-ID}}`\
`--header 'D360-API-KEY: ••••••`

{% hint style="info" %}
Replace <mark style="color:$success;">`{{CONSUMER-WHATSAPP-ID}}`</mark>  with the phone number of the WhatsApp user who you are requesting call permissions from.
{% endhint %}

The user will have the following view:&#x20;

<figure><img src="/files/5GbzRfsuA2jThtQbRREh" alt=""><figcaption></figcaption></figure>

### Collect CPR using a template message&#x20;

To personalize the request, you can alternatively use a template message to send a call permission request:

* [#create-cpr-template-message](#create-cpr-template-message "mention")
* [#send-cpr-template-message](#send-cpr-template-message "mention")

#### Create CPR Template Message

{% stepper %}
{% step %}

#### Create CPR template

You can use the endpoint \ <mark style="color:$success;">`POST`</mark> `/{WABA_ID}/message_templates`<br>

Adapt you request body using the following example

```json
{
  "name": "sample_cpr_template_test",
  "language": "en",
  "category": "MARKETING",
  "components": [
    {
      "type": "BODY",
      "text": "We would like to call you about Order {{1}} containing {{2}}. Can we call you?",
      "example": { "body_text": [["ON-12345", "Avocados"]] }
    },
    { "type": "FOOTER", "text": "Talk to you soon!" },
    { "type": "CALL_PERMISSION_REQUEST" }
  ]
}
```

{% endstep %}

{% step %}

#### You will get the following response

```json
{ "id": "<ID>", "status": "<STATUS>", "category": "<CATEGORY>" }re
```

{% endstep %}
{% endstepper %}

#### Send CPR Template Message

{% stepper %}
{% step %}

#### To send the template message, you can use the endpoint: &#x20;

<mark style="color:$success;">`POST`</mark> `/{PHONE_NUMBER_ID}/messages`

#### Request body

```json
{
  "messaging_product": "whatsapp",
  "to": "{{recipient_phone_number}}",
  "type": "template",
  "template": {
    "name": "sample_cpr_template_test",
    "language": { "code": "en" },
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "ON-12345" },
          { "type": "text", "text": "Avocados" }
        ]
      }
    ]
  }
}
```

{% endstep %}

{% step %}

#### When the user responds to the CPR you will receive the following webhook with the status:

* <mark style="color:$success;">`GRANTED`</mark>: End-user accepted the request and you can now start the call.&#x20;
* <mark style="color:$success;">`REJECTED`</mark>: End-user rejected the request. A new request can be sent.
* <mark style="color:$success;">`REVOKED`</mark>: The permission expired or was withdrawn by the end-user.

Webhook example:

```json
{
  "event": "call_permission_status",
  "status": "GRANTED | REJECTED | REVOKED",
  "waba_id": "<WABA_ID>",
  "recipient": "<phone_number>"
}
```

{% endstep %}
{% endstepper %}

{% hint style="info" %}

#### CPR rules:

* A permission is valid for **7 days**.
* Max **2 CPRs per 7 days per user**.
* Max **5 business-initiated calls per 24h per user**.
* Permission auto-revoked after **4 unanswered calls**.
  {% endhint %}

***

## How to Initiate a Call

{% stepper %}
{% step %}

### **Enable Callback Permission Status in call settings**

When <mark style="color:green;">`callback_permission_status`</mark> is enabled, the user automatically provides call permission to your business when they place a call to you.

{% hint style="info" %}

#### The call permission request **expires** when any of the following occurs:

* When the consumer interacts with a subsequent *new* call permission request to the business
* 7 days after the permission was accepted or declined by the consumer
* 7 days after the permission was delivered if the consumer does not respond to the request
  {% endhint %}
  {% endstep %}

{% step %}

### Initiate the Call

Once permission is granted, you can initiate the call.

**Endpoint:**&#x20;

<mark style="color:green;">`POST`</mark> `https://waba-v2.360dialog.io/calling/calls`

<mark style="color:green;">`--header 'D360-API-KEY: ••••••'`</mark>

```json
{
  "messaging_product": "whatsapp",
  "to": {recipient-phone-number},
  "action": "connect",
  "session": {
    "sdp_type": "offer",
    "sdp": "<<RFC 4566 SDP"
  }
}
```

{% endstep %}

{% step %}

#### If there are no errors, you will receive a successful response:

```json
{
  "messaging_product": "whatsapp",
  "calls" : [
     "id" : "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA", // The WhatsApp call ID
   ]
}
```

{% endstep %}

{% step %}

#### Once permission is granted, initiate a call:

#### Endpoint

<mark style="color:$success;">`POST`</mark> `/calling/calls/{phone_number_id}`

**Body**

```json
{
  "messaging_product": "whatsapp",
  "to": "{{recipient_phone_number}}",
  "action": "connect",
  "session": {
    "sdp_type": "offer",
    "sdp": "<<RFC 8866 SDP>>"
  }
}
```

{% endstep %}

{% step %}

#### After successful initiation of a new call, you will receive Call Connect Webhook response that contains SDP Answer from Cloud API.&#x20;

Your business will then apply the SDP Answer from this webhook to your WebRTC stack in order to initiate the media connection.

```json
{
    "entry": [
        {
            "changes": [
                {
                    "field": "calls",
                    "value": {
                        "calls": [
                            {
                                "biz_opaque_callback_data": "TRx334DUDFTI4Mj", // Arbitrary string passed by business for tracking purposes
                                "session": {
                                    "sdp_type": "answer",
                                    "sdp": "<RFC 8866 SDP>"
                                },
                                "from": "13175551399", // The business phone number placing the call (caller)
                                "connection": {
                                    "webrtc": {
                                        "sdp": "<RFC 8866 SDP>"
                                    }
                                },
                                "id": "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA", // The WhatsApp call ID
                                "to": "12185552828", // The WhatsApp user's phone number (callee)
                                "event": "connect",
                                "timestamp": "1749196895",
                                "direction": "BUSINESS_INITIATED"
                            }
                        ],
                        "metadata": { // ID and display number for the business phone number placing the call (caller)
                            "phone_number_id": "436666719526789",
                            "display_phone_number": "13175551399"
                        },
                        "messaging_product": "whatsapp"
                    }
                }
            ],
            "id": "366634483210360" // WhatsApp Business Account ID associated with the business phone number
        }
    ],
    "object": "whatsapp_business_account"
},
```

{% endstep %}

{% step %}

#### After initiating the call, you may receive these statuses:

* <mark style="color:green;">`RINGING`</mark>: Call is ringing on the user's device
* <mark style="color:green;">`ACCEPTED`</mark>: User accepted the call
* <mark style="color:green;">`REJECTED`</mark>: User rejected the call
* <mark style="color:green;">`TERMINATE`</mark>: Call ended for any reason

```json
{
  "entry": [
    {
      "changes": [
        {
          "field": "calls",
          "value": {
            "statuses": [
              {
                "id": "wacid.HBgLMTIxODU1NTI4MjgVAgARGCAyODRQIAFRoA", // The WhatsApp call ID
                "type": "call",
                "status": "[RINGING|ACCEPTED|REJECTED]", // The current call status
                "timestamp": "1749197000",
                "recipient_id": "12185552828" // The WhatsApp user's phone number (callee)
              }
            ],
            "metadata": { // ID and display number for the business phone number placing the call (caller)
              "phone_number_id": "436666719526789",
              "display_phone_number": "13175551399"
            },
            "messaging_product": "whatsapp"
          }
        }
      ],
      "id": "366634483210360" // WhatsApp Business Account ID associated with the business phone number
    }
  ],
  "object": "whatsapp_business_account"
}
```

{% endstep %}
{% endstepper %}

***

## Troubleshooting

If you face errors, make sure to check in the following table the possible resolutions. If you can't find your issue, please contact our support team for more.&#x20;

[Get support](/docs/support/get-support.md)

| Error code | Description                  | Resolution                           |
| ---------- | ---------------------------- | ------------------------------------ |
| 138006     | No approved call permission  | Request CPR before initiating a call |
| 138010     | CPR expired                  | Ask user again for permission        |
| 138011     | Too many CPR requests        | Respect 2 per 7d limit               |
| 138012     | Business call limit exceeded | Wait 24h or reduce attempts          |
| 138014     | Invalid SDP                  | Ensure SDP complies with RFC 8866    |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.360dialog.com/docs/messaging/calling/outbound-calls/how-to-handle-outbound-calls.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
