Zum Hauptinhalt springen

Vodia PBX JavaScript Voice Agents Functions Reference

Call Object Functions

FunctionDescriptionParametersExample
call.say()Text-to-speech playbacktext (string) or object with text, optional background, and optional callback propertiescall.say('Welcome to our service'); or call.say({ text: 'Hello', background: false, callback: function() { call.hangup(); } });
call.dtmf()Capture DTMF digitsCallback functioncall.dtmf(onDtmfHandler);
call.play()Play audioObject with audio, codec, direction properties or urlcall.play({ direction: "out", codec: codec, audio: audio });
call.stream()Start/stop audio streamingObject with codec, interval, callback or empty to stopcall.stream({ codec: "g711_ulaw", interval: 0.5, callback: streamHandler });
call.transfer()Transfer call to extension or control attended transferExtension number (string) or object with action property ('accept', 'reject', 'blind', 'hold', 'resume')call.transfer('700'); or call.transfer({ action: 'accept' }); or call.transfer({ action: 'hold' }); or call.transfer({ action: 'resume' });
call.find()Find active callsType (string), extension (string)const calls = call.find('calls', call.extension);
call.dial()Initialize AI integration or make outbound call. Returns a call ID that can be used with call.control()Provider name (string) or object with call parameterscall.dial('openai'); or var callid = call.dial({ account: '88', dest: '4001', from: caller, cobj: callObj, ivraction: 'attendant' });
call.control()Control a call previously initiated with call.dial(), e.g. cancel it before it is answeredObject with action property (currently 'cancel') and callid (the ID returned by call.dial())call.control({ action: 'cancel', callid: callid });
call.mute()Mute the callNonecall.mute();
call.hangup()Hang up the callNonecall.hangup();
call.sms()Send SMS messageString (text only) or object with text, from, to, name properties (optional)call.sms("Hello"); or call.sms({ text: "hello", from: "+16171234567", to: "+12121234567", name: "Customer" });
call.log()Log message to consoleMessage (string)call.log("Script started");
call.http()Register HTTP callback handler for webhooksCallback functioncall.http(onHttpHandler);

Call Object Properties

PropertyDescriptionTypeExample
call.callidUnique identifier for the callstringvar callId = call.callid;
call.langLanguage code for the callstringvar language = call.lang;
call.extensionCurrent extension handling the callstringvar ext = call.extension;
call.orig_fromOriginal FROM header of the callstringvar from = call.orig_from;
call.ivractionVoice Agents action type (e.g., 'attendant')stringif (call.ivraction == 'attendant') { ... }

System Object Functions

FunctionDescriptionParametersExample
system.http()Make HTTP requestObject with method, url, header, body, callback, timeoutsystem.http({ method: 'POST', url: 'http://...', callback: handler });
system.email()Send emailObject with to, from, subject, bodysystem.email({ to: recipient, from: sender, subject: 'Test', body: 'Message' });
system.parseEmailAdr()Parse email addressEmail stringvar addr = system.parseEmailAdr('test@example.com');

Tables Object Functions

FunctionDescriptionParametersExample
tables['cobjs'].get()Get call object datacallid, field name (e.g., 'from', 'to')var from = tables['cobjs'].get(call.callid, 'from');
tables['adrbook'].search()Search address bookField name, search valuevar results = tables['adrbook'].search('number', phoneNumber);
tables['adrbook'].get()Get address book entryEntry ID, field name or '*' for allvar fax = tables['adrbook'].get(entryId, 'fax');
tables['adrbook'].count()Count address book entriesNonevar count = tables['adrbook'].count();

AddressBook Class

While tables['adrbook'] provides low-level read access, the AddressBook class offers a convenient way to search, read, create, update and delete address book entries from a Voice Agents script.

tip

Writes performed with the AddressBook class during a call are reflected immediately — for example, an updated caller name will show up right away on the phone display and in the user front end.

FunctionDescriptionParametersExample
new AddressBook()Create an address book object for a tenant (and optionally a user)domain (string or number, required), user (string or number, optional)var adr = new AddressBook('localhost');
adr.search()Search for an entry. Without a field name, it tries number, mobile and fax. Searchable fields: number, mobile, fax, speed, tag, cmc, category. The callback receives the entry ID or false if not foundfield (string, optional), search value (string), callback functionadr.search('number', from, function(id) { ... }); or adr.search(from, function(id) { ... });
adr.get()Read entry content. Pass a field name (string) for one column, an array of field names for several, or omit for all columns as one objectid, field (string, array or omitted)var company = adr.get(id, 'company');
adr.update()Update an entry. fields is an object with column names and values. Display fields for numbers are generated automatically if not providedid, fields (object), callback function (optional)adr.update(id, { category: 'vip' }, function() { ... });
adr.create()Create a new entry, then apply the provided fieldsfields (object), callback function (optional)adr.create({ first: 'Joe', name: 'Doe', number: '+16173998147' }, function(id) { ... });
adr.erase()Delete an entryid, callback function (optional)adr.erase(id, function() { ... });

Common address book columns include number, mobile, fax, first, name, company, email, tag, cmc, comment, agent, dnc, melody and category. For the complete column list, see the Vodia Backend JavaScript Documentation.

note

AddressBook.search() supports only the indexed fields listed above — it does not search display_number. To match on display_number (the number as originally entered), use the low-level tables['adrbook'].search('display_number', value) instead, as shown in the Smart Address Book Transfer example.

note

On multi-tenant systems, pass the correct tenant to the constructor rather than hardcoding 'localhost'. The tenant name can be extracted from the to header of the call — see the Webhook Smart Routing example for a complete getDomainFromCall() implementation.

WebSocket Functions

FunctionDescriptionParametersExample
new Websocket()Create WebSocket connectionWebSocket URL (string)var ws = new Websocket("wss://api.example.com");
ws.header()Set WebSocket headersArray of header objects with name, value, optional secretws.header([{ name: "Authorization", value: "Bearer " + token, secret: true }]);
ws.on()Register event handlerEvent name ('open', 'close', 'message'), callback functionws.on('message', function(message) { ... });
ws.send()Send data through WebSocketData (string, typically JSON)ws.send(JSON.stringify(data));
ws.connect()Connect WebSocketNonews.connect();
ws.close()Close WebSocket connectionNonews.close();

Utility Functions

FunctionDescriptionParametersExample
toBase64String()Convert data to base64Data (buffer/string)var b64 = toBase64String(audioData);
fromBase64String()Convert base64 to dataBase64 stringvar data = fromBase64String(b64String);
setTimeout()Execute function after delayCallback function, delay in millisecondssetTimeout(function() { call.hangup(); }, 5000);
clearTimeout()Cancel scheduled timeoutTimer referenceclearTimeout(timer);
console.log()Log to consoleMessage (string)console.log("Debug message");

Common Patterns and Usage

DTMF Collection Pattern

var digits = '';
call.dtmf(onDtmf);

function onDtmf(digit) {
digits += digit;
if (digits.length == 4) {
processDigits(digits);
digits = '';
}
}

HTTP Request Pattern

var args = {
method: 'POST',
url: 'http://example.com/webhook',
header: [{ name: 'Content-Type', value: 'application/json' }],
body: JSON.stringify({ key: 'value' }),
callback: function(code, response, headers) {
console.log('Response code: ' + code);
var data = JSON.parse(response);
// Process response
}
};
system.http(args);

WebSocket Streaming Pattern (OpenAI Realtime)

var ws = new Websocket("wss://api.openai.com/v1/realtime?model=...");

ws.on('open', function() {
console.log("Connected");
});

ws.on('message', function(message) {
var msg = JSON.parse(message);
if (msg.type == "response.audio.delta") {
var audio = fromBase64String(msg.delta);
call.play({ direction: "out", codec: "g711_ulaw", audio: audio });
}
});

call.stream({
codec: "g711_ulaw",
interval: 0.5,
callback: function(audio) {
ws.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: toBase64String(audio)
}));
}
});

ws.connect();

Attended Transfer Pattern

// Step 1: In the originating call - find active calls
const calls = call.find('calls', call.extension);

if (calls.length > 0) {
const c = calls[0];

// Step 2: Hold the current call
call.transfer({ action: 'hold' });

// Step 3: Dial the destination with attended transfer parameters
// call.dial() returns a call ID that can be used with call.control()
var callid = call.dial({
account: '88', // Account/trunk to use
dest: '403', // Destination extension
from: '', // Can be empty or original caller
cobj: c.cobj, // Original call object to connect
ivraction: info // Information to pass (or 'attendant')
});
}

// Step 4: In the destination Voice Agents, handle the attended transfer
if (call.ivraction && call.ivraction == 'attendant') {
// Extract original caller information
const origname = call.orig_from.split('"')[1];

// Prompt destination user and get response
// Based on their response:

// Option A: Accept the transfer (connects both calls)
call.transfer({ action: 'accept' });

// Option B: Reject the transfer (returns caller to original call)
call.transfer({ action: 'reject' });
}

// Step 5: Handle transfer result via HTTP callback
call.http(function(args) {
var body = JSON.parse(args.body);

if (body.type == 'att_transfer') {
if (body.result == 'true') {
console.log('Attended transfer succeeded');
call.say({
text: 'Transfer successful',
callback: function() { call.hangup(); }
});
} else {
console.log('Attended transfer failed');
call.say({
text: 'Transfer failed',
callback: function() { call.hangup(); }
});
}
}
});

Attended Transfer with Timeout (Cancel) Pattern

When the voice agent dials a destination for an attended transfer, the destination may never accept or reject the call. Use the call ID returned by call.dial() together with call.control() to cancel the outbound leg and call.transfer({ action: 'resume' }) to take back the held call.

// Hold the current call and dial the transfer destination
call.transfer({ action: 'hold' });

var callid = call.dial({
account: '851',
dest: '403',
from: 'Some test',
cobj: call.callid,
ivraction: 'att_transfer'
});

// If the destination has not accepted or rejected within 20 seconds,
// cancel the dialed call and resume the held call
var timer = setTimeout(function() {
call.transfer({ action: 'resume' });
call.control({ action: 'cancel', callid: callid });
call.say('The party is not available right now.');
}, 20000);

// If the transfer completes in time, clear the timer in the
// att_transfer HTTP callback with clearTimeout(timer)

Table Lookup Pattern

// Get caller information (raw values are full SIP URIs)
var fromRaw = tables['cobjs'].get(call.callid, 'from');
var toRaw = tables['cobjs'].get(call.callid, 'to');

// Extract the number before searching — address book entries
// store numbers in global format (e.g. +61433997299), not SIP URIs
var match = fromRaw.match(/sip:(\+?\d+)@/);
var from = match ? match[1] : fromRaw;

// Search address book
var results = tables['adrbook'].search('number', from);
if (results && results.length > 0) {
var entryId = results[0];
var extension = tables['adrbook'].get(entryId, 'fax');
call.transfer(extension);
}

Address Book Read/Write Pattern

// Look up the caller and tag them as a VIP; if unknown, create a new entry.
// Changes are visible immediately on the phone display and user front end.
function extractPhoneNumber(sipUri) {
if (!sipUri) return '';
var match = sipUri.match(/sip:(\+?\d+)@/);
return (match && match[1]) ? match[1] : sipUri;
}

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

// Derive the tenant from the 'to' header (multi-tenant safe)
var domainMatch = toRaw.match(/@([^>]+)/);
var currentDomain = domainMatch ? domainMatch[1] : 'localhost';

var adr = new AddressBook(currentDomain);

adr.search('number', from, function(id) {
if (id) {
adr.update(id, { category: 'vip', comment: 'Called the priority line' }, function() {
call.transfer('400');
});
} else {
adr.create({ number: from, name: 'Unknown Caller', category: 'new' }, function(newId) {
call.transfer('501');
});
}
});

Call Transfer with Delay Pattern

call.say('Transferring your call now.');
setTimeout(function() {
call.transfer('700');
}, 3000);

SMS Sending Pattern

// Simple SMS
call.sms("Thank you for calling. Your reference number is 12345.");

// Advanced SMS with custom from/to numbers
call.sms({
text: "Your appointment is confirmed for tomorrow at 2 PM.",
from: "+16171234567",
to: "+12121234567",
name: "Customer"
});

AI Integration Pattern

// Initialize AI provider for the call
// Set up HTTP callback handler
call.http(onHttpCallback);

function onHttpCallback(args) {
var body = JSON.parse(args.body);

if (body.type == 'realtime.call.incoming') {
// Handle AI call setup
setupAICall(body.data.call_id);
}
}

call.dial('openai');

HTTP Callback Handler Pattern

// Register handler for incoming HTTP callbacks (webhooks)
call.http(function(args) {
console.log('Received HTTP callback');
console.log(JSON.stringify(args));

var body = JSON.parse(args.body);

// Process different callback types
if (body.type == 'realtime.call.incoming') {
var callid = body.data.call_id;

// Accept the incoming call
system.http({
method: 'POST',
url: 'https://api.openai.com/v1/realtime/calls/' + callid + '/accept',
header: [
{ name: 'Authorization', value: 'Bearer ' + apiKey, secret: true },
{ name: 'Content-Type', value: 'application/json' }
],
body: JSON.stringify({
type: "realtime",
model: "gpt-realtime",
instructions: "You are a helpful assistant."
}),
callback: function(code, response, headers) {
console.log('Call accepted with code: ' + code);
// Continue processing
}
});
}
else if (body.type == 'att_transfer') {
// Handle attended transfer result
if (body.result == 'true') {
console.log('Attended transfer succeeded');
call.say({ text: 'Transfer successful', callback: function() { call.hangup(); }});
} else {
console.log('Attended transfer failed');
call.say({ text: 'Transfer failed', callback: function() { call.hangup(); }});
}
}
});

Text-to-Speech with Callback Pattern

// Execute code after TTS completes
call.say({
text: 'Please wait while I transfer your call.',
background: false,
callback: function() {
// This runs after TTS finishes
call.transfer('700');
}
});

// Chain multiple TTS messages
call.say({
text: 'Thank you for calling.',
callback: function() {
call.say({
text: 'Goodbye.',
callback: function() {
call.hangup();
}
});
}
});

Audio Codecs

Supported audio codecs for call.play() and call.stream():

  • "g711_ulaw" - G.711 μ-law (most common)
  • "g711_alaw" - G.711 A-law
  • "pcm16" - 16-bit PCM
  • "mp3" - MP3 (for playback only)

Version Information

These functions are documented based on Vodia PBX version 69.5.19+. call.control(), the call ID return value of call.dial() and the 'resume' transfer action require a current v70 build. Function availability and behavior may vary in different versions.

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