Voice Broadcast API

View Release Notes →

Overview

The Broadcast API lets you send a single API request that dials out to a list of phone numbers simultaneously. Each recipient receives an outbound call and hears a personalised audio message — a pre-recorded file, text-to-speech, or SSML — the moment they answer. Your server is kept informed throughout via webhook events, and you can interact with each individual call leg while it is still active.

Broadcast is purpose-built for high-throughput, one-to-many voice delivery. Common use cases include:

  • Emergency and service alerts — notify customers of outages, cancellations, or critical updates
  • Appointment and delivery reminders — reduce no-shows by calling recipients directly
  • OTP and verification calls — read a one-time passcode over the phone as a fallback to SMS
  • Telemarketing and promotional campaigns — play a promotional message to a targeted recipient list
  • Political and civic campaigns — robocall constituents with recorded messages
  • Agricultural and rural outreach — reach audiences with low SMS literacy using voice

How Broadcast differs from the Core Voice API

The Core Voice API (POST /voice/v1/call) dials a single number per request and expects your webhook to drive the entire call flow interactively. The Broadcast API (POST /voice/v1/broadcast) accepts a list of recipients in one request, dials all of them at the same time, and automatically plays the configured audio the instant a call is answered. You do not need to serve a per-call webhook response to trigger playback — it starts on its own.

Once a broadcast call is connected and audio is playing, each call leg can still be individually controlled using the same per-call routes that exist in the Core Voice API: play additional audio, connect (transfer) the call, put the call on hold, start recording, stream audio over WebSocket, or hang up. Broadcast-specific webhook events carry an extra broadcast_id field that ties individual call legs back to the originating batch.

How It Works

Every broadcast session follows a predictable lifecycle. Understanding this flow is essential for writing a reliable webhook handler.

  1. You initiate the broadcast. Send a POST /voice/v1/broadcast request with your recipient list and the audio to play for each one. The API responds immediately with a broadcast_id and a state of broadcastcall_initiated. At this point, the calls have been queued — no actual dialling has happened yet.
  2. EnableX dials each recipient. The platform simultaneously dials every phone number in the recipients array. Each call gets its own voice_id which uniquely identifies that call leg for the rest of its lifetime.
  3. You receive a ringing webhook for each call. As soon as the outbound call starts ringing at the recipient's device, EnableX posts a ringing event to your event_url. The payload includes the voice_id for that call leg, a channel_id, and your original custom_data.
  4. The call is answered. When the recipient picks up, you receive a connected webhook. If Answering Machine Detection (AMD) is enabled, the call_answered_by field tells you whether the call was answered by a HUMAN or a MACHINE. You can use this to skip playback for voicemail answers if that suits your use case.
  5. Audio playback starts automatically. EnableX immediately begins playing the audio you configured in the request — there is no additional trigger needed from your side. You receive a playstate webhook with playstate: "initiated" confirming that the audio has started.
  6. Audio playback finishes. When the configured audio finishes playing, you receive a playstate webhook with playstate: "playfinished". This is the key event in the broadcast lifecycle — it is your signal to act. At this point you should either play a follow-up message (send PUT /voice/v1/call/{voice_id}/play) or hang up (DELETE /voice/v1/call/{voice_id}). If you do nothing, the call will remain open and idle.
  7. The call is disconnected. Whether you hang up explicitly or the recipient ends the call, you receive a disconnected webhook for that individual call leg. It includes the call duration in seconds and the Q.850 cause code for the disconnect reason.
  8. The entire session completes. Once every call leg in the broadcast has ended, EnableX fires a single broadcastcall_complete event. This is the terminal event for the session — no further events will follow. The payload contains a summary of every call in the batch, including duration, answer status, and disconnect reason.
Tip: Answering Machine Detection (AMD)

Set "answering_machine_detection": true in the initiation request to enable AMD. EnableX will then classify each answered call as HUMAN or MACHINE and report the result in the connected webhook's call_answered_by field. This lets you avoid leaving promotional messages on voicemail when that is not desired.

Authentication

The Broadcast API uses the same HTTP Basic Authentication as all EnableX Voice APIs. Encode your APP_ID and APP_KEY as a Base64 string and pass it in the Authorization header with every request.

Authorization: Basic base64(APP_ID:APP_KEY)

Your APP_ID and APP_KEY are available in the EnableX Portal under your application settings. For a full explanation of how to obtain and use these credentials, see the Voice Authentication page.

Important: Keep your APP_KEY secret

Never expose your APP_KEY in client-side code, mobile apps, or public repositories. All Broadcast API calls must originate from your server.

Base URL

All Broadcast API requests are made to the following base URL:

https://api.enablex.io

Append the versioned path to this base for each endpoint, for example https://api.enablex.io/voice/v1/broadcast.

Initiate Broadcast Call

This is the entry point for every broadcast session. A single call to this endpoint kicks off simultaneous outbound dialling to all the phone numbers in your recipients list. Each recipient can have their own audio configuration — you are not locked into playing the same message to everyone in the batch.

Use this endpoint when you want to trigger a batch of outbound voice calls at the same moment: think appointment reminders sent to all tomorrow's patients, or an emergency alert that needs to reach a contact list immediately.

The request body has a top-level structure that covers the campaign metadata and caller settings, with a recipients array that holds each phone number and its individual play configuration. The play object inside each recipient entry is where you choose the audio type: a hosted audio file URL, a pre-uploaded prompt by name, a text-to-speech string, or a full SSML document. Each audio type has slightly different required fields, which the variants below make clear.

POST /voice/v1/broadcast
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json

Request body — play.type: "url" (using a URL)

Use this when your audio file is hosted at a publicly accessible HTTPS URL. EnableX will fetch and stream the file when the call is answered. All Asterisk-supported audio formats are accepted.

{
  "name": "TEST_APP",
  "owner_ref": "XYZ",
  "from": "12012774877",
  "answering_machine_detection": false,
  "recipients": [
    {
      "to": "918088394833",
      "play": {
        "type": "url",
        "url": "https://example.com/audio.mp3",
        "prompt_ref": "prompt_f1"
      }
    }
  ],
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "event_url": "http://your-server:6000/event",
  "call_handler_url": "http://your-server:6000/event"
}

Request body — play.type: "file" (using a pre-uploaded prompt name)

Use this when you have already uploaded the audio file to EnableX and stored it under a named prompt. This avoids the latency of an external HTTP fetch at call time, and is preferable for high-volume campaigns where the same audio is sent to many recipients.

{
  "name": "TEST_APP",
  "owner_ref": "XYZ",
  "from": "12012774877",
  "answering_machine_detection": false,
  "recipients": [
    {
      "to": "918088394833",
      "play": {
        "type": "file",
        "prompt_name": "welcome_prompt",
        "prompt_ref": "prompt_f1"
      }
    }
  ],
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "event_url": "http://your-server:6000/event",
  "call_handler_url": "http://your-server:6000/event"
}

Request body — play.type: "tts" (text-to-speech)

Use this to have EnableX synthesise speech from a text string at call time. You specify the language and voice gender. This is ideal for personalised messages where the content changes per recipient — for example, reading the recipient's name or a booking reference number.

{
  "name": "TEST_APP",
  "owner_ref": "XYZ",
  "from": "12012774877",
  "answering_machine_detection": false,
  "recipients": [
    {
      "to": "918088394833",
      "play": {
        "type": "tts",
        "text": "Hi Welcome",
        "voice": "Female",
        "language": "en-US",
        "prompt_ref": "prompt_f1"
      }
    }
  ],
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "event_url": "http://your-server:6000/event",
  "call_handler_url": "http://your-server:6000/event"
}

Request body — play.type: "ssml" (SSML markup)

Use this for precise control over synthesis — pauses, emphasis, digit-by-digit reading, specific neural voice names, and more. Pass a complete SSML document as the ssml string. This is the most expressive option and is well-suited for multi-lingual messages or legally required verbatim disclosures.

{
  "name": "TEST_APP",
  "owner_ref": "XYZ",
  "from": "12012774877",
  "answering_machine_detection": false,
  "recipients": [
    {
      "to": "918088394833",
      "play": {
        "type": "ssml",
        "ssml": "<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xml:lang=\"en-IN\"><voice name=\"en-IN-NeerjaNeural\">Press <say-as interpret-as=\"digits\">9</say-as> for English.</voice></speak>",
        "prompt_ref": "prompt_f1"
      }
    }
  ],
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "event_url": "http://your-server:6000/event",
  "call_handler_url": "http://your-server:6000/event"
}

Request fields

Field Type Required Description
name string Yes Application or campaign name. Used for reporting and identification in the portal.
owner_ref string Yes Owner reference identifier. An arbitrary string you assign to correlate this broadcast with a record in your system.
from string Yes Caller ID presented to recipients, in E.164 format (e.g. 12012774877). Must be a number provisioned in your EnableX account.
answering_machine_detection boolean No Enable AMD to classify answered calls as HUMAN or MACHINE. Default: false.
recipients array Yes List of recipient objects. Each object defines the phone number to dial and the audio to play.
recipients[].to string Yes Recipient phone number in E.164 format (e.g. 918088394833).
recipients[].play object Yes Play configuration for this specific recipient. Defines what audio is played when the call is answered.
recipients[].play.type string Yes Audio type. One of: file, tts, ssml.
recipients[].play.url string Conditional Publicly accessible URL of the audio file. Required when type=file and prompt_name is not provided.
recipients[].play.prompt_name string Conditional Name of a pre-uploaded audio prompt stored in your EnableX account. Required when type=file and url is not provided.
recipients[].play.text string Conditional Text string to synthesise as speech. Required when type=tts.
recipients[].play.voice string Conditional TTS voice gender: Female or Male. Required when type=tts.
recipients[].play.language string Conditional BCP-47 language tag for TTS synthesis (e.g. en-US, en-IN). Required when type=tts.
recipients[].play.ssml string Conditional Full SSML markup string. Required when type=ssml.
recipients[].play.prompt_ref string Yes A reference ID you assign to this play action. EnableX echoes this value back in all playstate webhook events, allowing you to correlate events to specific audio in your campaign logic.
custom_data object No Arbitrary key-value pairs. EnableX echoes this object verbatim in every webhook event for this broadcast session, so you can tie events back to your application's data model without a separate lookup.
event_url string Yes The HTTPS URL where EnableX posts all webhook events for this broadcast (ringing, connected, playstate, disconnected, broadcastcall_complete).
call_handler_url string Yes URL for call handler logic. Handles per-call control instructions for the broadcast session.

Response — 200 OK

The API responds synchronously as soon as the broadcast has been accepted and queued. The calls have not yet been dialled at this point — that happens asynchronously. The response tells you the broadcast_id for the session and categorises each recipient number into one of three buckets: call_initiated (dialling will proceed), call_blacklisted (number is on a DNC or blacklist), or country_not_allowed (your account is not enabled to dial that country code).

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "state": "broadcastcall_initiated",
  "from": "12012774877",
  "to": {
    "call_initiated": [
      {
        "to": "918088394833",
        "play": {
          "type": "file",
          "url": "...",
          "prompt_ref": "prompt_f1"
        }
      }
    ],
    "call_blacklisted": [],
    "country_not_allowed": []
  },
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "timestamp": "2026-04-08T15:24:10.670Z"
}
Field Type Description
broadcast_id string (UUID) Unique identifier for this broadcast session. Present in every subsequent webhook event for this batch.
state string Always broadcastcall_initiated in this response, confirming the session has been accepted.
to.call_initiated array Recipients that will be dialled. Each entry echoes the to number and play configuration from the request.
to.call_blacklisted array Numbers that were blocked because they appear on a DNC (Do Not Call) or blacklist. No call will be made to these numbers.
to.country_not_allowed array Numbers blocked because outbound calling to that country is not enabled for your account.
custom_data object The custom_data object you passed in the request, echoed back verbatim.
timestamp string ISO 8601 UTC timestamp of when the broadcast was accepted.
Note: Save your broadcast_id

Store the broadcast_id from this response. It ties all subsequent webhook events for this session together, and you will need it to correlate per-call events to the originating batch in your database.

Per-Call Controls

Once a broadcast is initiated and calls start connecting, each individual call leg has its own voice_id — a UUID assigned by EnableX and delivered to your server in the ringing and connected webhook events. Using this voice_id as a path parameter, you can control each call independently while it is active.

These endpoints mirror the Core Voice API's per-call routes. The difference in a broadcast context is that every response will also carry a broadcast_id field, tying the action back to the originating batch.

broadcast_id in Webhook Notifications

When a Control API is called on a broadcast call leg, the resulting webhook notification will include a broadcast_id field in the payload. This allows your server to identify that the call belongs to a specific broadcast batch and handle it accordingly — for example, updating campaign-level metrics or routing the event to a batch-specific handler.

Tip: When to use per-call controls

The most common trigger for per-call actions is the playstate: "playfinished" webhook. When you receive that event, extract the voice_id and either play a follow-up message or disconnect the call. Leaving the call idle after audio finishes will result in open, silent calls that consume channel capacity.

Play Audio

After the initial audio has finished, use this endpoint to play a follow-up message on the same live call. The call must still be connected — you will typically call this in response to the playstate: "playfinished" webhook to queue the next audio segment in a multi-step campaign.

The request body accepts the same four audio variants as the initiation request (file URL, file prompt_name, tts, ssml). The example below shows a TTS follow-up:

PUT /voice/v1/call/{voice_id}/play
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
{
  "play": {
    "type": "tts",
    "text": "Thank you for calling. Goodbye.",
    "voice": "Female",
    "language": "en-US",
    "prompt_ref": "prompt_f2"
  },
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 }
}

Response — 200 OK

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "to": "918088394833",
  "playstate": "initiated",
  "statusCode": 200,
  "timestamp": "2026-04-08T15:24:13.926Z"
}

A playstate: "initiated" response confirms the new audio has started. You will receive a corresponding playstate: "playfinished" webhook when it completes.

Connect Call

Transfer an active broadcast call leg to another party. This is useful when a recipient responds positively to a broadcast prompt and you want to connect them live with an agent or a secondary IVR flow, without requiring the recipient to call back on a separate number.

PUT /voice/v1/call/{voice_id}/connect
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
{
  "from": "12012774877",
  "to": "918088394833"
}
Field Type Description
from string Caller line identity (CLI) number to present to the connect destination. E.164 format.
to string Destination phone number to connect the call to. E.164 format.

Response — 200 OK

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "state": "connecting",
  "timestamp": "2026-04-08T15:24:15.000Z"
}

A state of connecting means the transfer has been initiated. The call leg will be bridged once the destination answers.

Hold / Unhold

Place an active call on hold or resume it. Holding a call pauses the audio stream to the recipient — useful if you need a moment to prepare the next action (such as fetching personalised content) before playing the next segment.

PUT /voice/v1/call/{voice_id}/hold
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json

To place the call on hold:

{ "hold": true }

To resume the call from hold:

{ "hold": false }
Field Type Required Description
hold boolean Yes true to place the call on hold; false to resume it.

Response — 200 OK

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "hold": true,
  "state": "success",
  "timestamp": "2026-04-08T15:24:16.000Z"
}

Recording

Start or stop recording on a single active call leg within the broadcast. Use this to capture the recipient's response when your broadcast includes a prompt asking them to leave a message, or when compliance requirements mandate a record of the interaction.

PUT /voice/v1/call/{voice_id}/recording
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json

To start recording, provide a recording_name to identify the file later:

{
  "start": true,
  "recording_name": "my_voice_recording"
}

To stop an active recording:

{ "stop": true }
Important: start and stop are mutually exclusive

Do not include both start and stop in the same request. Send one or the other depending on the action you want to take.

Response — recording started

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "recording_name": "my_voice_recording",
  "state": "recording_started",
  "timestamp": "2026-04-08T15:24:17.000Z"
}

Response — recording stopped

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "state": "recording_stopped",
  "timestamp": "2026-04-08T15:24:22.000Z"
}

Stream Audio

Connect the audio of an active broadcast call leg to a WebSocket endpoint for real-time processing. This is useful when you want to stream a recipient's spoken response to a speech-to-text engine, or inject synthesised speech back into the call dynamically — for example, to build an interactive post-broadcast follow-up flow.

The WebSocket URL must use the secure wss:// scheme. Once the stream is started, EnableX begins sending audio frames to your WebSocket server. See the Media Streaming page for full details on the WebSocket message protocol.

PUT /voice/v1/call/{voice_id}/stream
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
{ "wss_host": "wss://your-websocket-url/ws" }
Field Type Required Description
wss_host string Yes WebSocket URL to stream call audio to. Must use the wss:// (secure WebSocket) scheme. Plain ws:// is not accepted.

Response — 200 OK

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "state": "stream_started",
  "wss_host": "wss://your-websocket-url/ws",
  "timestamp": "2026-04-08T15:24:18.000Z"
}

Disconnect Call

Terminate an individual call leg within the broadcast session. This is the correct way to hang up a specific recipient's call without affecting the other active calls in the same batch. You will typically call this in your webhook handler after the playstate: "playfinished" event, once your message has been fully delivered.

No request body is required.

DELETE /voice/v1/call/{voice_id}
Authorization: Basic base64(APP_ID:APP_KEY)

Response — 200 OK

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "state": "success",
  "timestamp": "2026-04-08T15:24:25.826Z"
}

After this call succeeds, you will receive a disconnected webhook event for the terminated call leg, and eventually a broadcastcall_complete event once all other legs in the session have also ended.

Webhook Events

EnableX delivers all broadcast events by posting JSON payloads to the event_url you specified in the initiation request. Your endpoint must respond with HTTP 200 within a reasonable timeout to acknowledge receipt. Events are delivered in call-event order for each individual call leg.

Every broadcast webhook event shares a common set of fields. The table below describes these fields — they appear in every event type documented on this page.

Field Type Description
broadcast_id string (UUID) Identifies the broadcast session this event belongs to. Matches the broadcast_id from the initiation response.
voice_id string (UUID) Identifies the individual call leg. Use this as the path parameter in all per-call control endpoints.
from string The caller ID used for this call leg.
to string The recipient phone number for this call leg.
custom_data object The custom_data object from the initiation request, echoed verbatim in every event.
timestamp string ISO 8601 UTC timestamp of when this event was generated.

ringing

Fired as soon as the outbound call starts ringing at the recipient's device. This is the first per-call event you receive after broadcast initiation. The voice_id in this payload is the identifier you will use in all subsequent per-call control requests for this call leg.

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "from": "1",
  "to": "918088394833",
  "state": "ringing",
  "channel_id": "102dcfdb-8087-45ac-b580-eeea53c247b8",
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "timestamp": "2026-04-08T15:24:10.746Z"
}

channel_id is an internal channel identifier used by EnableX infrastructure. state: "ringing" confirms the call is actively ringing — no action is required from your server at this point.

connected

Fired when the recipient answers the call. If AMD is enabled, the call_answered_by field tells you whether a human or an answering machine picked up. You can use this to suppress the audio message for machine-answered calls (e.g. by immediately calling DELETE .../call) if your campaign policy does not allow voicemail drops.

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "from": "1",
  "to": "918088394833",
  "state": "connected",
  "call_answered_by": "HUMAN",
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "timestamp": "2026-04-08T15:24:13.742Z"
}

call_answered_by is HUMAN or MACHINE. This field is only present when AMD is enabled for the broadcast.

playstate — initiated

Fired when audio playback begins on the call. This event confirms the audio has started streaming to the recipient. The prompt_ref value matches what you set in the play configuration, allowing you to identify which audio segment is currently playing if you have multiple prompt references in a session.

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "to": "918088394833",
  "playstate": "initiated",
  "prompt_ref": "prompt_f1",
  "timestamp": "2026-04-08T15:24:13.752Z"
}

playstate — playfinished

Fired when audio playback has fully completed. This is the most important event in the broadcast webhook lifecycle — it is your signal to take the next action on the call. Once you receive this event, you have two choices: play a follow-up audio segment (call PUT .../play) or terminate the call (call DELETE .../call). If you take no action, the call will remain open and silent, consuming channel capacity.

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "to": "918088394833",
  "playstate": "playfinished",
  "prompt_ref": "prompt_f2",
  "prompt_name": "prompt_f2",
  "timestamp": "2026-04-08T15:24:20.753Z"
}
Important: Always act on playstate:playfinished

Your webhook handler must respond to every playstate: "playfinished" event. Either queue the next audio with PUT /voice/v1/call/{voice_id}/play or hang up with DELETE /voice/v1/call/{voice_id}. Leaving a call idle after playback ends wastes channel capacity and may incur additional usage charges.

disconnected

Fired when an individual call leg ends — whether you hung it up explicitly, the recipient ended the call, or the network caused an abnormal disconnect. The payload includes the call duration in seconds and the Q.850 disconnect cause code, which you can use to analyse delivery success rates across your campaign.

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
  "from": "1",
  "to": "918088394833",
  "state": "disconnected",
  "call_duration": 12.089,
  "disconnect_reason": "Normal Clearing",
  "disconnect_cause_code": 16,
  "call_answered_by": "HUMAN",
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "timestamp": "2026-04-08T15:24:25.831Z"
}

call_duration is in seconds (floating-point). disconnect_cause_code is the ITU-T Q.850 cause code — see the Disconnect Cause Codes table below. call_answered_by is only present when AMD is enabled.

broadcastcall_complete

Fired once every single call leg in the broadcast session has terminated. This is the terminal event for the entire batch — no further events will follow after this. Use it to mark the campaign as completed in your system, trigger post-campaign analytics, or notify your operations team.

The payload contains a broadcast_call_info array that provides a per-call summary of every leg in the batch: duration, final state, answer classification, and disconnect reason.

{
  "broadcast_id": "74a2c2c2-4d55-4b0d-a9f6-8ec09f3bcc35",
  "state": "broadcastcall_complete",
  "blacklistednumber": {
    "call_blacklisted": [],
    "country_not_allowed": []
  },
  "broadcast_call_info": [
    {
      "voice_id": "bc273d94-cb59-4f66-8eba-e1f96a7e482e",
      "to": "918088394833",
      "callDuration": 12.089,
      "callState": "Call Answered",
      "call_answered_by": "HUMAN",
      "disconnectReason": "Normal Clearing",
      "disconnectReasonCode": 16
    }
  ],
  "custom_data": { "booking_id": 12345, "farmer_id": 6789 },
  "timestamp": "2026-04-08T15:24:27.941Z"
}
Field Type Description
blacklistednumber.call_blacklisted array Numbers that were blocked at initiation time due to DNC/blacklist rules. Echoed here for a complete session record.
blacklistednumber.country_not_allowed array Numbers that were blocked at initiation time because their country code is not enabled for your account.
broadcast_call_info array Summary of every call leg in the batch. One entry per recipient that was actually dialled.
broadcast_call_info[].voice_id string (UUID) The individual call leg identifier.
broadcast_call_info[].to string Recipient phone number for this call leg.
broadcast_call_info[].callDuration number Total call duration in seconds.
broadcast_call_info[].callState string Final state of the call (e.g. Call Answered).
broadcast_call_info[].call_answered_by string AMD result: HUMAN or MACHINE. Present when AMD was enabled.
broadcast_call_info[].disconnectReason string Human-readable disconnect reason (e.g. Normal Clearing).
broadcast_call_info[].disconnectReasonCode integer Q.850 disconnect cause code. See the Disconnect Cause Codes table.
Note: broadcastcall_complete is the terminal event

Once you receive broadcastcall_complete, no further webhook events will be delivered for this broadcast session. It is safe to close out any session state you are tracking at this point.

Disconnect Cause Codes

The disconnect_cause_code field in the disconnected webhook and the disconnectReasonCode field in broadcastcall_complete use Q.850 cause codes — the ITU-T standard for ISDN/PSTN call clearing reasons. The table below lists the codes you are most likely to encounter in broadcast campaigns.

Code Reason What it means for your campaign
16 Normal Clearing The call ended cleanly — either you hung up via the API or the recipient ended the call after hearing the message.
17 User Busy The recipient's line was busy at the time of the call. Consider a retry strategy for these numbers.
18 No User Responding The call timed out waiting for the remote end to respond. Network issue on the far side.
19 No Answer from User The phone rang but was not answered before the ring timeout expired.
21 Call Rejected The recipient's device or carrier actively rejected the call. This can also indicate the number is on DND (Do Not Disturb).

Important Notes

  • All timestamps are ISO 8601 UTC. Every timestamp field across all webhook events and API responses uses the format YYYY-MM-DDTHH:mm:ss.sssZ in UTC.
  • Phone numbers must be E.164 format. Both from and all to fields must use E.164 notation — the country code followed by the national number with no spaces, dashes, or parentheses (e.g. 918088394833 not +91-80883-94833).
  • custom_data is echoed verbatim. Whatever object you pass in custom_data at initiation will be included unchanged in every webhook event for the entire session — ringing, connected, playstate, disconnected, and broadcastcall_complete. Use this to carry session context (booking IDs, customer references, campaign tags) through to your webhook handler without a database lookup.
  • Act on playstate:playfinished. When your event_url receives a playstate: "playfinished" event, you must either call PUT /voice/v1/call/{voice_id}/play to queue the next audio or DELETE /voice/v1/call/{voice_id} to hang up. Failing to act leaves the call open and silent.
  • broadcastcall_complete is the terminal event. After this event fires, the broadcast session is fully closed. No additional webhook events will be delivered. It is safe to release all state associated with the broadcast_id.
  • start and stop in the recording endpoint are mutually exclusive. Send either { "start": true, "recording_name": "..." } or { "stop": true } — never both in the same request body.
  • wss_host must use the wss:// scheme. The streaming endpoint requires a secure WebSocket URL. Plain ws:// connections are rejected. Ensure your WebSocket server has a valid TLS certificate.
  • Per-call controls use Core Voice API routes. After a broadcast is initiated, each individual call leg is controlled via /voice/v1/call/{voice_id}/... routes — the same routes used by the Core Voice API. The broadcast context is preserved through the broadcast_id field included in all responses and events.