Mon-Fri 10am - 6pm
Voice Broadcast with Text to Speech
Base URL for your account: https://cpaas.studio
GET
POST
/api/voice/v1/broadcast/tts
Sends a broadcast whose audio is spoken from text. Two modes — send exactly one: (1) template_id + variables: an approved TTS template from your account, with a value for each of its variables; open to every voice.broadcast key. (2) text: free text; only for keys with audio auto-approval. Asynchronous: the response is 202 "preparing" — poll Broadcast with TTS Status. Every different message is a new audio: it costs TTS credits and takes a few seconds (up to about 30 for some voices) before dialling starts.
Parameters
| Param | Type | Required | Notes |
|---|---|---|---|
did |
string | yes | Same as Voice Broadcast: the phone number of one of your active broadcast DIDs. |
numbers |
array or string | yes | Same as Voice Broadcast. Maximum 1,000 numbers per request. In template mode every number hears the same values. |
template_id |
string | one of template_id/text | Template mode. The id of an approved template (TTS Templates lists them, e.g. TPL7K2M9QX4WQ). Not with text. |
variables |
object | template mode | One value per template variable: JSON {"name": "Anil"}, or variables[name]=Anil in a query string / form. Each value 1–30 characters after trimming, no line breaks or { }. Spoken as typed. Keys the template does not use are ignored. |
text |
string | one of template_id/text | Free-text mode — keys with audio auto-approval only. The message to speak, up to 5,000 characters. Not with template_id. |
language |
string | no | Free text: language code (e.g. en, hi, ml), default en; must be enabled for your account. Template mode: the template's language is used — if you send language it must be the same. |
gender |
string | yes | female or male. If the language has no voice of that gender, the available voice is used. |
speed |
number | no | 0.5–2.0. Defaults to 1.0. |
schedule_at |
string (date) | no | As Voice Broadcast. If the time passes while the audio is being prepared, the broadcast is sent immediately. |
ring_time / retry_count / retry_interval |
integer | no | As Voice Broadcast. |
reference |
string, max 64 | no | Your label; also set on the broadcast (and, for free text, the audio asset). Status can be looked up by it. |
name |
string, max 255 | no | Broadcast label. Defaults to reference or a generated label. |
audio_uid / audio_reference / caller_id |
— | do not supply | Rejected on this endpoint — the audio comes from the text or template, the caller id from did. |
Request example
curl -X POST "https://cpaas.studio/api/voice/v1/broadcast/tts" \
-H "X-API-KEY: cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8b1e0c4a-2f7d-4e59-9a61-3c5d2e7f9b10" \
-d '{
"did": "9876500000",
"numbers": ["9876543210", "9876543211"],
"template_id": "TPL7K2M9QX4WQ",
"variables": {"customer_name": "Anil", "amount": "1500"},
"gender": "female",
"reference": "bill-4471"
}'
<?php
$ch = curl_init('https://cpaas.studio/api/voice/v1/broadcast/tts');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-KEY: cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'Idempotency-Key: 8b1e0c4a-2f7d-4e59-9a61-3c5d2e7f9b10',
'Content-Type: application/json',
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'did' => '9876500000',
'numbers' => ['9876543210', '9876543211'],
'template_id' => 'TPL7K2M9QX4WQ',
'variables' => ['customer_name' => 'Anil', 'amount' => '1500'],
'gender' => 'female',
'reference' => 'bill-4471',
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
# One number, its own values — e.g. an OTP. Each different code is a new audio.
curl "https://cpaas.studio/api/voice/v1/broadcast/tts?did=9876500000&numbers=9876543210&template_id=TPL3OTP8K2M4Q&variables[code]=4%208%202%201&gender=female" \
-H "X-API-KEY: cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Free text — keys with audio auto-approval only.
curl -X POST "https://cpaas.studio/api/voice/v1/broadcast/tts" \
-H "X-API-KEY: cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d "did=9876500000" \
-d "numbers=9876543210,9876543211" \
-d "text=Your order 4471 has been shipped. Thank you for shopping with us." \
-d "language=en" -d "gender=female" -d "speed=1" \
-d "reference=order-4471"
Success response (data)
{
"id": "TTB4M8Q2ZK7RX",
"reference": "bill-4471",
"status": "preparing",
"tts": {
"language": "en",
"gender": "female",
"speed": 1
},
"credits_estimated": {
"voice.broadcast": 2,
"voice.tts": 7
},
"template": {
"id": "TPL7K2M9QX4WQ",
"name": "Bill reminder"
},
"audio": null,
"items": {
"total": 1,
"ready": 0,
"failed": 0
},
"broadcast": null,
"error": null,
"created_at": "2026-10-05T13:10:00+00:00",
"completed_at": null
}
Errors
| Code | Status | When |
|---|---|---|
TTS_TEMPLATE_NOT_FOUND |
404 | template_id doesn't match any template on your account. |
TTS_TEMPLATE_NOT_APPROVED |
422 | The template is pending or rejected. error.details.status says which. |
TTS_TEMPLATE_VARIABLE_MISSING |
422 | A template variable has no value. error.details.variables lists every variable with a problem (name, problem, message). |
TTS_TEMPLATE_VARIABLE_INVALID |
422 | A value is longer than 30 characters, has a line break or control character, or contains { }. error.details.variables says which. |
TTS_AUTO_APPROVAL_REQUIRED |
403 | Free text with a key that does not have audio auto-approval. Use a template instead. |
TTS_LANGUAGE_UNAVAILABLE |
422 | The language (yours, or the template's) is not enabled for your account. error.details.available_languages lists the ones that are. |
BCAST_DID_INVALID |
422 | did doesn't match any of your account's active, voice.broadcast-enabled DIDs. |
BCAST_NO_VALID_NUMBERS |
422 | Every submitted number was invalid or a duplicate. |
BCAST_ALL_NUMBERS_INVALID |
422 | Every submitted number failed format normalization. |
BILLING_INSUFFICIENT_CREDITS |
402 | Your voice.broadcast or voice.tts balance does not cover this request. error.details shows needed vs balance for each. |
REQ_SCHEDULE_IN_PAST |
422 | schedule_at is already in the past. |
REQ_IDEMPOTENCY_KEY_CONFLICT |
409 | The Idempotency-Key matches a prior request with a different body (including different variables). |
REQ_VALIDATION_FAILED |
422 | Both or neither of text/template_id, language different from the template's, missing gender/did/numbers, audio_uid/audio_reference/caller_id supplied, or any other field rule failure. |
Notes
- Asynchronous: 202 means accepted and preparing — the broadcast does not exist yet. Poll Broadcast with TTS Status with the returned id (or your reference).
- Every different message is a new audio: it costs TTS credits (per word) and takes a few seconds before dialling — about 1–2 s on hosted voices, up to about 30 s on some Indian-language voices. Sending exactly the same message again (same text/values, language, gender, speed) reuses the audio with no TTS charge.
- OTP and other per-number messages: send one request per number with its own variables. Every code is a new audio, so expect the TTS charge and the delay on each one.
- Template mode: template is {id, name} and audio is always null — the audio is internal. Free text: template is null and audio is {audio_uid, reused}; the audio stays in your Audio Library (type broadcast).
- Once status is "submitted", the response contains the broadcast (same shape as Voice Broadcast) — use its id with Voice Broadcast Number Status for per-number results. A number whose audio could not be prepared shows status no_audio there: not dialled, not charged.
- If preparation fails, status is "failed" with error.code: TTS_GENERATION_FAILED (no TTS credits charged), AUDIO_DELIVERY_FAILED, TTS_UNAVAILABLE, TTS_TEMPLATE_NOT_APPROVED (template edited while preparing), BILLING_INSUFFICIENT_CREDITS, BCAST_DID_INVALID, etc. Send a new request to try again.
- Credits are checked up front for both voice.tts (per word) and voice.broadcast (per number), and charged as they are used — TTS when the audio is made, broadcast per chunk as calls go out.
Need more context? See Getting started for authentication, the response envelope, and the full error-code reference.