Zum Hauptinhalt springen

Voice Agents JS Example (Webhook Smart Routing)

Voice Agents JS Example - Webhook Smart Routing with Dynamic Address Book

This example script demonstrates Vodia's Javascript Voice Agents functionality for intelligent call routing via external webhook integration. The script extracts caller information, queries an external API for routing decisions, dynamically updates the address book with caller details, and transfers calls to the designated destination.

Scenario:

  • Extract caller ID and called number from SIP URI format (from and to headers).
  • Automatically detect the domain/tenant using the Domain API.
  • Send caller information to an external webhook for routing decisions.
  • Receive routing destination and caller name from webhook response.
  • Dynamically create or update address book entries with caller information.
  • Play customizable TTS message before transfer.
  • Transfer call to webhook-specified destination.
  • Comprehensive error handling for webhook failures.
'use strict';

function extractPhoneNumber(sipUri) {
if (!sipUri) return '';
var match = sipUri.match(/sip:(\+?\d+)@/);
if (match && match[1]) return match[1];
return sipUri;
}

function getDomainFromCall() {
// Extract domain from the 'to' field
var toRaw = tables['cobjs'].get(call.callid, 'to');
var domainMatch = toRaw.match(/@([^>]+)/);
var domainName = domainMatch ? domainMatch[1] : 'localhost';

console.log('Extracted domain: ' + domainName);

// Get domain index using Domain API
try {
var domainIndex = Domain.get(domainName, "*");
console.log('Domain index: ' + domainIndex);
return domainName;
} catch (e) {
console.log('Error getting domain: ' + e);
return 'localhost';
}
}

var fromRaw = tables['cobjs'].get(call.callid, 'from');
var toRaw = tables['cobjs'].get(call.callid, 'to');
var from = extractPhoneNumber(fromRaw);
var to = extractPhoneNumber(toRaw);
var currentDomain = getDomainFromCall();

console.log('=== CALL START ===');
console.log('From: ' + from);
console.log('To: ' + to);
console.log('Domain: ' + currentDomain);
console.log('Call ID: ' + call.callid);

call.say('Please wait while we route your call.');

var args = {
method: 'POST',
url: 'http://170.64.177.251:5000/webhook',
header: [
{ name: 'Content-Type', value: 'application/json' },
{ name: 'Connection', value: 'keep-alive' }
],
body: JSON.stringify({
callerNumber: from,
calledNumber: to,
callId: call.callid,
domain: currentDomain
}),
timeout: 10000,
callback: function(code, response) {
console.log('=== WEBHOOK RESPONSE ===');
console.log('Code: ' + code);
console.log('Body: ' + response);

if (code == 200) {
try {
var res = JSON.parse(response);

if (!res.destination) {
console.log('No destination provided');
call.say('Unable to route. Goodbye.');
setTimeout(function() { call.hangup(); }, 3000);
return;
}

var processTransfer = function() {
if (res.callerName) {
console.log('Caller name provided: ' + res.callerName);
updateAddressBook(currentDomain, from, res.callerName, function() {
console.log('Transferring to: ' + res.destination);
call.transfer(res.destination);
});
} else {
console.log('No caller name, transferring to: ' + res.destination);
call.transfer(res.destination);
}
};

if (res.message) {
call.say({
text: res.message,
callback: processTransfer
});
} else {
processTransfer();
}

} catch (e) {
console.log('Parse error: ' + e);
call.say('An error occurred.');
setTimeout(function() { call.hangup(); }, 3000);
}
} else {
console.log('Webhook failed with code: ' + code);
call.say('Service unavailable.');
setTimeout(function() { call.hangup(); }, 3000);
}
}
};

console.log('Sending webhook request...');
system.http(args);

function updateAddressBook(domain, phoneNumber, callerName, callback) {
console.log('=== ADDRESS BOOK UPDATE ===');
console.log('Domain: ' + domain);
console.log('Phone: ' + phoneNumber);
console.log('Name: ' + callerName);

var nameParts = callerName.split(' ');
var firstName = nameParts[0] || '';
var lastName = nameParts.slice(1).join(' ') || '';

console.log('First: ' + firstName + ', Last: ' + lastName);

// Create AddressBook object for the domain
var adr = new AddressBook(domain);
console.log('AddressBook object created');

// Search for existing entry
adr.search(phoneNumber, function(id) {
console.log('Search callback - ID: ' + (id || 'not found'));

if (id) {
// Update existing entry
console.log('Updating existing entry ID: ' + id);

adr.update(id, {
first: firstName,
name: lastName,
display_number: phoneNumber
}, function() {
console.log('Address book entry updated successfully');
if (callback) callback();
});

} else {
// Create new entry
console.log('Creating new address book entry');

adr.create({
number: phoneNumber,
display_number: phoneNumber,
first: firstName,
name: lastName,
tag: 'auto-ivr'
}, function(newId) {
console.log('Address book entry created - ID: ' + newId);
if (callback) callback();
});
}
});
}

External Webhook Request Format

The script sends the following JSON payload to the external webhook:

{
"callerNumber": "+61433337285",
"calledNumber": "61272010747",
"callId": 9,
"domain": "phones.pbx70.vodia-teams.com"
}

Expected Webhook Response Format

The external webhook should return a JSON response with the following structure:

{
"destination": "500",
"message": "Routing your call to customer service",
"callerName": "John Smith"
}

Response Fields:

  • destination (required) - Extension or phone number to transfer the call to
  • message (optional) - Text-to-speech message to play before transfer
  • callerName (optional) - Caller's name to store in the address book

Call Flow

  1. Incoming Call - Caller dials IVR extension
  2. Information Extraction - Extract caller/called numbers and domain
  3. Webhook Query - Send caller details to external API
  4. Response Processing - Parse destination and caller information
  5. Address Book Update - Create or update entry with caller name
  6. TTS Message (optional) - Play custom message to caller
  7. Transfer - Connect caller to destination extension
  8. Name Display - Destination phone shows caller name from address book

Configuration

Replace the webhook URL with your external service endpoint:

url: 'http://170.64.177.251:5000/webhook',  // Change to your webhook URL
tip

The webhook response's message field is optional. If omitted, the call transfers immediately without additional TTS playback. Use this for faster routing when no announcement is needed.

tip

Address book entries created by this script are tagged with 'auto-ivr' for easy identification and management. You can query or delete these entries using this tag.

note

The callerName field in the webhook response should be the full name. The script automatically splits it into first and last name components for proper address book storage.

note

Ensure your external webhook responds within the 10-second timeout window. Consider implementing webhook retry logic on the external server for improved reliability.

warning

The webhook URL is called for every incoming call. Ensure your external service can handle the expected call volume and has appropriate rate limiting and security measures in place.

For more information on Vodia's JavaScript capabilities, refer to: Vodia Backend JavaScript Documentation