Skip to main content

DTMF Control

DTMF (Dual-Tone Multi-Frequency) tones allow you to interact with calls using keypad input. This enables features like IVR menus, navigating automated systems, and capturing user input.

What is DTMF?

DTMF is the standard telephone signaling method that produces the tones when you press numbers (0-9) or symbols (* and #) on a phone keypad. Each key produces a unique combination of two frequencies.

┌─────┬─────┬─────┬─────┐
│ 1 │ 2 │ 3 │ A │
├─────┼─────┼─────┼─────┤
│ 4 │ 5 │ 6 │ B │
├─────┼─────┼─────┼─────┤
│ 7 │ 8 │ 9 │ C │
├─────┼─────┼─────┼─────┤
│ * │ 0 │ # │ D │
└─────┴─────┴─────┴─────┘

Receiving DTMF Input

Use the gather DTMF action to collect user input:

curl -X POST https://api.audian.com:8443/v2/calls/call_xyz789/gather-dtmf \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Press 1 for billing, 2 for support, or 3 for sales.",
"max_digits": 1,
"timeout": 10
}'

Response:

{
"call_id": "call_xyz789",
"digits_collected": "2",
"input_type": "dtmf",
"collected_at": "2024-01-15T10:31:00Z"
}

Gather Parameters

ParameterTypeDefaultDescription
promptstring-Message to play before collecting input
max_digitsinteger1Maximum digits to collect
timeoutinteger10Seconds to wait for input
finish_keystring#Key that finishes input collection
languagestringen-USLanguage for TTS prompt
voicestringfemaleVoice gender for TTS
interrupt_on_dtmfbooleantrueAllow user to interrupt prompt by pressing digit

Using Pre-Recorded Prompts

Instead of TTS, play a recorded audio file:

curl -X POST https://api.audian.com:8443/v2/calls/call_xyz789/gather-dtmf \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"audio_url": "https://example.com/menu-prompt.mp3",
"max_digits": 2,
"timeout": 10,
"finish_key": "#"
}'

Collecting Multiple Digits

Collect longer sequences of input:

curl -X POST https://api.audian.com:8443/v2/calls/call_xyz789/gather-dtmf \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Please enter your account number followed by the pound key.",
"max_digits": 15,
"timeout": 30,
"finish_key": "#"
}'

Response:

{
"call_id": "call_xyz789",
"digits_collected": "123456789012345",
"finish_key_pressed": "#",
"collected_at": "2024-01-15T10:31:00Z"
}

Handling DTMF Timeouts

If no input is received within the timeout period:

{
"call_id": "call_xyz789",
"digits_collected": null,
"timeout": true,
"timed_out_at": "2024-01-15T10:31:15Z"
}

Handle timeouts and retry:

async function collectWithRetry(callId, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await gatherDTMF(callId, {
prompt: `Please enter your selection. This is attempt ${i + 1}.`,
max_digits: 1,
timeout: 10
});

if (response.digits_collected) {
return response.digits_collected;
}
} catch (error) {
console.error('Error gathering DTMF:', error);
}
}

// All retries failed
return null;
}

Sending DTMF Tones

Send DTMF tones to the call destination (useful for automating interactions with other systems):

curl -X POST https://api.audian.com:8443/v2/calls/call_xyz789/send-dtmf \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"digits": "1234",
"duration": 100,
"pause_between": 100
}'

Send DTMF Parameters

ParameterTypeDefaultDescription
digitsstring-DTMF digits to send
durationinteger100Milliseconds per digit
pause_betweeninteger100Milliseconds between digits

Example: Navigating an Automated System

# Call an automated system and navigate to extension 5
curl -X POST https://api.audian.com:8443/v2/calls \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+14155552671",
"to": "+14155552672",
"call_control": {
"on_answered": [
{
"action": "play_audio",
"message": "Navigating to extension 5"
},
{
"action": "send_dtmf",
"digits": "5",
"duration": 100
}
]
}
}'

DTMF Routing

Route calls based on DTMF input during call setup:

curl -X POST https://api.audian.com:8443/v2/calls \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+14155552671",
"to": "+14155552672",
"call_control": {
"on_answered": [
{
"action": "play_audio",
"message": "Press 1 for sales, 2 for support, 3 for billing."
},
{
"action": "gather_dtmf",
"max_digits": 1,
"timeout": 10,
"routes": [
{
"digit": "1",
"action": "transfer",
"destination": "+14155552673"
},
{
"digit": "2",
"action": "transfer",
"destination": "+14155552674"
},
{
"digit": "3",
"action": "transfer",
"destination": "+14155552675"
},
{
"digit": "*",
"action": "repeat_menu"
}
]
}
]
}
}'

DTMF Events

Monitor DTMF input through webhooks:

ivr.input_received

{
"event": "ivr.input_received",
"data": {
"call_id": "call_xyz789",
"menu_id": "menu_main",
"input": "2",
"input_type": "dtmf",
"received_at": "2024-01-15T10:31:00Z"
}
}

ivr.timeout

{
"event": "ivr.timeout",
"data": {
"call_id": "call_xyz789",
"menu_id": "menu_main",
"timeout_duration": 30,
"timed_out_at": "2024-01-15T10:31:30Z"
}
}

DTMF Validation

Validate DTMF input for common use cases:

function validateDTMF(input, rules) {
// Validate format (digits, length, etc.)
if (!input || input.match(/[^0-9#*]/)) {
return {
valid: false,
error: 'Input contains invalid characters'
};
}

if (rules.minLength && input.length < rules.minLength) {
return {
valid: false,
error: `Minimum length is ${rules.minLength}`
};
}

if (rules.maxLength && input.length > rules.maxLength) {
return {
valid: false,
error: `Maximum length is ${rules.maxLength}`
};
}

if (rules.allowedDigits) {
const isValid = input.split('').every(digit =>
rules.allowedDigits.includes(digit)
);
if (!isValid) {
return {
valid: false,
error: `Only these digits are allowed: ${rules.allowedDigits.join(', ')}`
};
}
}

return { valid: true };
}

// Usage
const result = validateDTMF('123', {
minLength: 1,
maxLength: 5,
allowedDigits: ['1', '2', '3', '4', '5']
});

Common DTMF Patterns

Account Lookup

curl -X POST https://api.audian.com:8443/v2/calls/call_xyz789/gather-dtmf \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Please enter your 6-digit account number.",
"max_digits": 6,
"timeout": 20,
"finish_key": "#"
}'

PIN Verification

curl -X POST https://api.audian.com:8443/v2/calls/call_xyz789/gather-dtmf \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Please enter your 4-digit PIN.",
"max_digits": 4,
"timeout": 15,
"suppress_log": true
}'
curl -X POST https://api.audian.com:8443/v2/calls/call_xyz789/gather-dtmf \
-H "X-Auth-Token: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Press 1 to repeat this menu, 2 for the main menu, or 0 to speak with an agent.",
"max_digits": 1,
"timeout": 10,
"interrupt_on_dtmf": true
}'

Error Handling

No Input Detected

{
"error": "no_input",
"message": "No DTMF input detected",
"code": 408
}

Invalid Digits

{
"error": "invalid_input",
"message": "Invalid DTMF input detected",
"code": 400
}

Call Disconnected

{
"error": "call_ended",
"message": "Call ended while waiting for DTMF input",
"code": 503
}

Code Examples

Node.js - Menu with Retry

const axios = require('axios');

async function handleMenuWithRetry(callId, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
console.log(`Attempt ${attempt}`);

const response = await axios.post(
`https://api.audian.com:8443/v2/calls/${callId}/gather-dtmf`,
{
prompt: 'Press 1 for sales, 2 for support, or 3 for billing.',
max_digits: 1,
timeout: 10
},
{
headers: {
'Authorization': `Bearer ${process.env.AUDIAN_API_KEY}`
}
}
);

if (response.data.digits_collected) {
return response.data.digits_collected;
}

if (attempt < maxAttempts) {
// Offer to retry
await axios.post(
`https://api.audian.com:8443/v2/calls/${callId}/play-audio`,
{
message: 'Sorry, I did not hear a valid selection. Please try again.',
participant: 'callee'
},
{
headers: {
'Authorization': `Bearer ${process.env.AUDIAN_API_KEY}`
}
}
);
}
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error.response?.data);
}
}

return null;
}

Python - Account Lookup

import requests
import os

def lookup_account(call_id):
url = f'https://api.audian.com:8443/v2/calls/{call_id}/gather-dtmf'
headers = {
'Authorization': f"Bearer {os.getenv('AUDIAN_API_KEY')}",
'Content-Type': 'application/json'
}
payload = {
'prompt': 'Please enter your 6-digit account number.',
'max_digits': 6,
'timeout': 20,
'finish_key': '#'
}

try:
response = requests.post(url, json=payload, headers=headers)
data = response.json()

if data.get('digits_collected'):
account_number = data['digits_collected']
print(f'Account: {account_number}')
return account_number
else:
print('Timeout - no input received')
return None

except requests.exceptions.RequestException as e:
print(f'Error: {e}')
return None

Best Practices

  1. Clear prompts: Make menu options clear and concise
  2. Allow interruption: Let users interrupt prompts with DTMF
  3. Set proper timeouts: 10-30 seconds depending on use case
  4. Provide feedback: Confirm selections back to the user
  5. Handle retries: Offer 2-3 attempts before connecting to agent
  6. Validate input: Check input format and length
  7. Suppress logging: Don't log sensitive input (PINs, account numbers)

Next Steps