Direct Survey
POST /api/v1/surveys/direct-order
Start a voice survey without a pre-built template. You send the survey tree in the request: optional start voices, the question, what each keypad option does (play voices or transfer to an agent), and optional end voices. Every voice slot takes either an approved voice from your voice library or a CDN audio URL.
Request Parameters
Section titled “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. |
| sender | string | Yes | Your active caller sender number |
| phone_numbers | array | Yes | Array of Bangladeshi phone numbers (01XXXXXXXXX format, 1-999 numbers). No duplicates allowed. |
| start_voices | array | No | Voice entries (0-10) played in sequence before the question |
| question_voices | array | Yes | Voice entries (1-10) for the question, played in sequence |
| invalid_voice | voice entry | No | Played when the caller presses a key that matches no option, before the question repeats. If omitted, the question repeats after silence. |
| end_voices | array | No | Voice entries (0-10) played at the end of the call |
| dtmf_options | array | Yes | Keypad options (1-9 items) |
| dtmf_options[].key | string | Yes | Keypad key, 1-9 (0, *, # not allowed). Keys must be unique. |
| dtmf_options[].option_type | string | No | voice (default) or transfer |
| dtmf_options[].voices | array | No | Voice entries (0-10) played after the caller picks this option |
| dtmf_options[].transfer_numbers | array | For transfer |
Numbers to transfer the caller to |
| dtmf_options[].ringback_voice | voice entry | No | Transfer only: played while connecting |
| dtmf_options[].all_busy_voice | voice entry | No | Transfer only: played when every transfer number is busy |
| metadata | object | No | Custom data to associate with the survey. Returned in results and the webhook payload. |
| webhook_url | string | No | URL to receive a webhook when the survey completes |
| config.retry_count | number | No | Retry attempts for failed calls, 0-3 (default 0) |
Voice entries
Section titled “Voice entries”Every voice field accepts entries in either form, and you can mix them in the same array:
{ "type": "voice", "name": "welcome-intro" } // approved voice from your voice library"https://cdn.example.com/audio/part1.wav" // CDN audio URL (needs extra permission)Voice-library references
nameis the voice name shown in your dashboard voice library (List Voices returns them). Matching ignores case.- The voice must be approved, not archived, and fully static — voices with dynamic parts are rejected.
- A multi-part voice expands to all of its parts, played in order. The 1-10 limit on array fields counts your request entries, before expansion.
- In single-file fields (
invalid_voice,ringback_voice,all_busy_voice) the voice must have exactly one part.
Audio URLs
- Must be WAV, 8000 Hz, mono,
pcm_s16le(G.711-compatible). Every URL is downloaded and validated before the survey starts; invalid files are rejected with400. - Require extra permission on your account (see above).
Any other entry type (for example {"type": "tts"}) is reserved and currently fails with 422.
Request Example
Section titled “Request Example”curl -X POST https://api.awajdigital.com/api/v1/surveys/direct-order \ -H "Authorization: Bearer your_api_token_here" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "request_id": "unique_direct_survey_123", "sender": "8801234567890", "phone_numbers": ["019XXXXXXXX", "018XXXXXXXX"], "start_voices": [{ "type": "voice", "name": "welcome-intro" }], "question_voices": [{ "type": "voice", "name": "delivery-question" }], "invalid_voice": { "type": "voice", "name": "invalid-key" }, "end_voices": [{ "type": "voice", "name": "thank-you" }], "dtmf_options": [ { "key": "1", "voices": [{ "type": "voice", "name": "order-confirmed" }] }, { "key": "2", "option_type": "transfer", "transfer_numbers": ["017XXXXXXXX"], "ringback_voice": { "type": "voice", "name": "please-wait" }, "all_busy_voice": { "type": "voice", "name": "agents-busy" } } ], "metadata": { "campaign_id": "delivery_confirm_2026" }, "webhook_url": "https://your-domain.com/webhook", "config": { "retry_count": 1 } }'// Browser JavaScript (Fetch API)const response = await fetch('https://api.awajdigital.com/api/v1/surveys/direct-order', { method: 'POST', headers: { 'Authorization': 'Bearer your_api_token_here', 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: 'unique_direct_survey_123', sender: '8801234567890', phone_numbers: ['019XXXXXXXX', '018XXXXXXXX'], start_voices: [{ type: 'voice', name: 'welcome-intro' }], question_voices: [{ type: 'voice', name: 'delivery-question' }], invalid_voice: { type: 'voice', name: 'invalid-key' }, end_voices: [{ type: 'voice', name: 'thank-you' }], dtmf_options: [ { key: '1', voices: [{ type: 'voice', name: 'order-confirmed' }] }, { key: '2', option_type: 'transfer', transfer_numbers: ['017XXXXXXXX'], ringback_voice: { type: 'voice', name: 'please-wait' }, all_busy_voice: { type: 'voice', name: 'agents-busy' } } ], metadata: { campaign_id: 'delivery_confirm_2026' }, webhook_url: 'https://your-domain.com/webhook', config: { retry_count: 1 } })});
const data = await response.json();console.log(data);// Node.js with axiosconst axios = require('axios');
async function createDirectSurvey() { try { const response = await axios.post('https://api.awajdigital.com/api/v1/surveys/direct-order', { request_id: 'unique_direct_survey_123', sender: '8801234567890', phone_numbers: ['019XXXXXXXX', '018XXXXXXXX'], start_voices: [{ type: 'voice', name: 'welcome-intro' }], question_voices: [{ type: 'voice', name: 'delivery-question' }], invalid_voice: { type: 'voice', name: 'invalid-key' }, end_voices: [{ type: 'voice', name: 'thank-you' }], dtmf_options: [ { key: '1', voices: [{ type: 'voice', name: 'order-confirmed' }] }, { key: '2', option_type: 'transfer', transfer_numbers: ['017XXXXXXXX'], ringback_voice: { type: 'voice', name: 'please-wait' }, all_busy_voice: { type: 'voice', name: 'agents-busy' } } ], metadata: { campaign_id: 'delivery_confirm_2026' }, webhook_url: 'https://your-domain.com/webhook', config: { retry_count: 1 } }, { 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); }}
createDirectSurvey();# Python with requestsimport requests
def create_direct_survey(): url = 'https://api.awajdigital.com/api/v1/surveys/direct-order' headers = { 'Authorization': 'Bearer your_api_token_here', 'Accept': 'application/json', 'Content-Type': 'application/json' } data = { 'request_id': 'unique_direct_survey_123', 'sender': '8801234567890', 'phone_numbers': ['019XXXXXXXX', '018XXXXXXXX'], 'start_voices': [{'type': 'voice', 'name': 'welcome-intro'}], 'question_voices': [{'type': 'voice', 'name': 'delivery-question'}], 'invalid_voice': {'type': 'voice', 'name': 'invalid-key'}, 'end_voices': [{'type': 'voice', 'name': 'thank-you'}], 'dtmf_options': [ {'key': '1', 'voices': [{'type': 'voice', 'name': 'order-confirmed'}]}, { 'key': '2', 'option_type': 'transfer', 'transfer_numbers': ['017XXXXXXXX'], 'ringback_voice': {'type': 'voice', 'name': 'please-wait'}, 'all_busy_voice': {'type': 'voice', 'name': 'agents-busy'} } ], 'metadata': {'campaign_id': 'delivery_confirm_2026'}, 'webhook_url': 'https://your-domain.com/webhook', 'config': {'retry_count': 1} }
try: response = requests.post(url, json=data, headers=headers) print(response.json()) except requests.exceptions.RequestException as e: print(f'Error: {e}')
create_direct_survey()<?php// PHP with cURL$url = 'https://api.awajdigital.com/api/v1/surveys/direct-order';$token = 'your_api_token_here';
$data = [ 'request_id' => 'unique_direct_survey_123', 'sender' => '8801234567890', 'phone_numbers' => ['019XXXXXXXX', '018XXXXXXXX'], 'start_voices' => [['type' => 'voice', 'name' => 'welcome-intro']], 'question_voices' => [['type' => 'voice', 'name' => 'delivery-question']], 'invalid_voice' => ['type' => 'voice', 'name' => 'invalid-key'], 'end_voices' => [['type' => 'voice', 'name' => 'thank-you']], 'dtmf_options' => [ ['key' => '1', 'voices' => [['type' => 'voice', 'name' => 'order-confirmed']]], [ 'key' => '2', 'option_type' => 'transfer', 'transfer_numbers' => ['017XXXXXXXX'], 'ringback_voice' => ['type' => 'voice', 'name' => 'please-wait'], 'all_busy_voice' => ['type' => 'voice', 'name' => 'agents-busy'] ] ], 'metadata' => ['campaign_id' => 'delivery_confirm_2026'], 'webhook_url' => 'https://your-domain.com/webhook', 'config' => ['retry_count' => 1]];
$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);?>With the URL permission enabled, any entry can be a CDN URL instead, mixed freely with library voices:
"question_voices": [ "https://cdn.example.com/audio/question_part1.wav", { "type": "voice", "name": "question-part-2" }]Success Response Example
Section titled “Success Response Example”{ "success": true, "survey": { "id": 789, "name": "direct_order_v1_1_1705312800000", "status": "surveying", "totalCount": 2, "createdAt": "2026-09-25T14:30:00.000+06:00", "metadata": { "campaign_id": "delivery_confirm_2026" } }}Poll Get Survey Result for per-number responses, or set webhook_url to be notified on completion.
Error Responses
Section titled “Error Responses”| Status | Description |
|---|---|
| 400 | Duplicate phone numbers or DTMF keys; audio URL not in G.711-compatible format; voice not found / not approved / archived; voice has dynamic parts; multi-part voice in a single-file field |
| 402 | Insufficient account balance |
| 403 | Audio URL entries sent without the URL permission, or sender not found / not active |
| 409 | request_id already used within 15 minutes |
| 422 | Invalid request parameters (including tts or unknown voice entry types) |
Voice and audio problems are reported together in a single 400, one item per field in errors:
{ "success": false, "message": "Audio file validation failed. Files must be in G.711-compatible format (WAV, 8000Hz, Mono, pcm_s16le)", "errors": [ { "field": "question_voices[1]", "issue": "voice \"greeting-v2\" not found or not approved" }, { "field": "dtmf_options[0].voices[0]", "issue": "voice \"promo\" has dynamic parts; only fully static voices are allowed" } ]}URL entries without the permission return 403 in the same shape:
{ "success": false, "message": "Direct audio URLs are not enabled for your account. Use approved voices ({\"type\": \"voice\", \"name\": \"...\"}) or contact support.", "errors": [ { "field": "question_voices[0]", "issue": "direct audio URLs require the send-direct-survey permission" } ]}