# Dynamic Broadcast

`POST /api/broadcasts/dynamic`

Send a personalized broadcast using a **dynamic voice** — an approved voice with variable parts (name, amount, etc.) filled in per recipient. Processing is asynchronous: the API accepts the request, then creates one broadcast per recipient in the background.

:::note[Dashboard setup required]
Before using this endpoint, create a dynamic voice in the dashboard and wait until it is approved. Then use that approved voice's name in the `voice` field.
:::

:::caution[Permission required]
If any required dynamic part of the voice uses `tts`, your account needs the AI TTS permission. Contact support to enable it. Requests without it receive `403`. `audio_url` dynamic parts do not need this permission.
:::

## Request Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| request_id | string | Yes | Unique request identifier (16-64 chars) to prevent duplicate requests. Use UUID or random string. |
| voice | string | Yes | Name of an approved voice owned by your account (1-255 chars). The voice must have dynamic parts with configured dynamic keys. |
| sender | string | Yes | Your active caller sender number (1-20 chars) |
| recipients | array | Yes | 1-10 recipient objects (see below). No duplicate phone numbers. |

### Recipient object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| phone_number | string | Yes | Bangladeshi phone number, `01XXXXXXXXX` format. |
| data | object | Yes | Key/value pairs (string values, max 5000 chars each by default). Every dynamic key required by the voice must be present and non-empty. |

## Dynamic parts

Dynamic voices support parts in these modes: `tts` (text spoken by AI TTS), `number`, `character`, `digit`, and `audio_url` (a CDN audio URL played in place). `audio_url` values are downloaded and validated as G.711-compatible WAV (8000 Hz, mono, `pcm_s16le`) during processing; an invalid file marks the request as failed.

## Request Example

```bash
curl -X POST https://api.awajdigital.com/api/broadcasts/dynamic \
  -H "Authorization: Bearer your_api_token_here" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "request_id": "unique_dynamic_broadcast_001",
    "voice": "due_reminder_dynamic",
    "sender": "8801234567890",
    "recipients": [
      {
        "phone_number": "019XXXXXXXX",
        "data": { "customer_name": "রহিম উদ্দিন", "due_amount": "1250" }
      },
      {
        "phone_number": "018XXXXXXXX",
        "data": { "customer_name": "করিম শেখ", "due_amount": "3400" }
      }
    ]
  }'
```

```javascript
// Browser JavaScript (Fetch API)
const response = await fetch('https://api.awajdigital.com/api/broadcasts/dynamic', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer your_api_token_here',
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    request_id: 'unique_dynamic_broadcast_001',
    voice: 'due_reminder_dynamic',
    sender: '8801234567890',
    recipients: [
      {
        phone_number: '019XXXXXXXX',
        data: { customer_name: 'রহিম উদ্দিন', due_amount: '1250' }
      },
      {
        phone_number: '018XXXXXXXX',
        data: { customer_name: 'করিম শেখ', due_amount: '3400' }
      }
    ]
  })
});

const data = await response.json();
console.log(data);
```

```javascript
// Node.js with axios
const axios = require('axios');

async function sendDynamicBroadcast() {
  try {
    const response = await axios.post('https://api.awajdigital.com/api/broadcasts/dynamic', {
      request_id: 'unique_dynamic_broadcast_001',
      voice: 'due_reminder_dynamic',
      sender: '8801234567890',
      recipients: [
        {
          phone_number: '019XXXXXXXX',
          data: { customer_name: 'রহিম উদ্দিন', due_amount: '1250' }
        },
        {
          phone_number: '018XXXXXXXX',
          data: { customer_name: 'করিম শেখ', due_amount: '3400' }
        }
      ]
    }, {
      headers: {
        'Authorization': 'Bearer your_api_token_here',
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }
    });

    console.log(response.data);
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
  }
}

sendDynamicBroadcast();
```

```python
# Python with requests
import requests

def send_dynamic_broadcast():
    url = 'https://api.awajdigital.com/api/broadcasts/dynamic'
    headers = {
        'Authorization': 'Bearer your_api_token_here',
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    }
    data = {
        'request_id': 'unique_dynamic_broadcast_001',
        'voice': 'due_reminder_dynamic',
        'sender': '8801234567890',
        'recipients': [
            {
                'phone_number': '019XXXXXXXX',
                'data': { 'customer_name': 'রহিম উদ্দিন', 'due_amount': '1250' }
            },
            {
                'phone_number': '018XXXXXXXX',
                'data': { 'customer_name': 'করিম শেখ', 'due_amount': '3400' }
            }
        ]
    }

    try:
        response = requests.post(url, json=data, headers=headers)
        print(response.json())
    except requests.exceptions.RequestException as e:
        print(f'Error: {e}')

send_dynamic_broadcast()
```

```php
<?php
// PHP with cURL
$url = 'https://api.awajdigital.com/api/broadcasts/dynamic';
$token = 'your_api_token_here';

$data = [
    'request_id' => 'unique_dynamic_broadcast_001',
    'voice' => 'due_reminder_dynamic',
    'sender' => '8801234567890',
    'recipients' => [
        [
            'phone_number' => '019XXXXXXXX',
            'data' => ['customer_name' => 'রহিম উদ্দিন', 'due_amount' => '1250']
        ],
        [
            'phone_number' => '018XXXXXXXX',
            'data' => ['customer_name' => 'করিম শেখ', 'due_amount' => '3400']
        ]
    ]
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token,
    'Accept: application/json',
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
print_r($result);
?>
```

## Success Response Example

```json
{
  "success": true,
  "message": "Request accepted",
  "data": {
    "request_id": "unique_dynamic_broadcast_001"
  }
}
```

The `202` response means the request was accepted, not that calls have started. Processing creates one broadcast per recipient, all sharing the same `request_id`. There is no dedicated status endpoint — poll [List Broadcasts](/api-docs/broadcasts/list) with `?request_id=<request_id>` until the broadcasts appear, then use [Get Broadcast Result](/api-docs/broadcasts/result) for each ID. If processing fails, the List Broadcasts response includes the failure in `data.error`.

## Error Responses

| Status | Description |
| --- | --- |
| 400 | Duplicate recipient phone numbers, voice has no dynamic parts / configured keys, or missing dynamic data for a recipient |
| 402 | Insufficient account balance |
| 403 | Voice not found / not approved, sender not found / not active, or missing AI TTS permission for `tts` dynamic parts |
| 422 | Invalid request parameters (`request_id` length, phone format, recipients array size, data value length) |
| 500 | Request acceptance failure, or a retry of a previously failed `request_id` |

:::note
Re-sending the same `request_id` for an already accepted request returns `202` with message `Request already accepted` (idempotent). If the stored request failed, retrying the same `request_id` returns `500` with message `Request failed` and the original error — use a new `request_id` instead.

Call minutes are billed per your pulse rate, same as any broadcast.
:::