Skip to main content

Voice Agents JS Example (EMAIL + CALENDAR)

Voice Agents JS Example - EMAIL with Calendar Invite

This example extends the basic email example by adding support for attaching an iCalendar (.ics) invite to outbound emails. When the external application returns appointment details, the email will include a calendar attachment that the recipient can accept directly in Outlook or Gmail, as well as a fallback "Add to Google Calendar" link in the email body.

Scenario:

  • Play a welcome message using Vodia's TTS.
  • Collect a 4-digit customer number from the user via DTMF.
  • Send the collected digits to an external application in JSON format.
  • If the JSON response includes an email address, send the details to that address.
    • If the response also includes event details (start, end), attach a calendar invite to the email.
  • If the response includes a transfer instruction, read the message aloud and transfer the call to the specified destination.
  • If neither condition is met, transfer the call to Auto Attendant extension 10.
'use strict';

call.say('Welcome to company ABC. Please enter your 4 digit customer number and we will email your details to the nominated email address.');

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

function ondtmf(digit) {
digits += digit;
if (digits.length == 4) {
send(digits);
digits = '';
}
}

/**
* Generates an ICS calendar invite string.
* @param {object} event - { uid, summary, description, location, start, end }
* start/end are JS Date objects or ISO 8601 strings.
* @returns {string} ICS file content
*/
function buildIcs(event) {
function toIcsDate(d) {
var date = (d instanceof Date) ? d : new Date(d);
return date.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
}

var uid = event.uid || ('event-' + Date.now() + '@companyabc.com');
var start = toIcsDate(event.start);
var end = toIcsDate(event.end);
var stamp = toIcsDate(new Date());

return [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//Company ABC//IVR//EN',
'METHOD:REQUEST',
'BEGIN:VEVENT',
'UID:' + uid,
'DTSTAMP:' + stamp,
'DTSTART:' + start,
'DTEND:' + end,
'SUMMARY:' + (event.summary || 'Appointment'),
'DESCRIPTION:' + (event.description || ''),
'LOCATION:' + (event.location || ''),
'ORGANIZER;CN=Company ABC Support:mailto:support@companyabc.com',
'END:VEVENT',
'END:VCALENDAR'
].join('\r\n');
}

function send(digits) {
var body = JSON.stringify({ customerCode: digits });

var args = {
method: 'POST',
url: 'https://external_server/webhook',
header: [{ name: 'Content-Type', value: 'application/json' }],
body: body,
callback: callback
};

system.http(args);

function callback(code, response, headers) {
console.log('Webhook code: ' + code.toString());
console.log('Webhook response: ' + response.toString());

var res = JSON.parse(response);

// If there's an email address in the response, send details
if (res.email) {
var emailArgs = {
to: system.parseEmailAdr(res.email),
from: { name: 'Company ABC Support', adr: 'support@companyabc.com' },
subject: 'Your Details Requested',
html: res.message
};

// If the response includes event details, attach a calendar invite
if (res.event && res.event.start && res.event.end) {
var icsContent = buildIcs({
uid: 'cust-' + digits + '-' + Date.now() + '@companyabc.com',
summary: res.event.summary || 'Appointment with Company ABC',
description: res.event.description || res.message || '',
location: res.event.location || '',
start: res.event.start,
end: res.event.end
});

emailArgs.attachments = [{
id: 'calendar-invite',
type: 'text/calendar; method=REQUEST; name="invite.ics"',
content: icsContent
}];

// Append a fallback "Add to Google Calendar" link in the email body
var gcalBase = 'https://calendar.google.com/calendar/render?action=TEMPLATE';
var gcalLink = gcalBase
+ '&text=' + encodeURIComponent(res.event.summary || 'Appointment')
+ '&dates=' + res.event.start.replace(/[-:]/g, '').replace(/\.\d{3}Z/, 'Z')
+ '/' + res.event.end.replace(/[-:]/g, '').replace(/\.\d{3}Z/, 'Z')
+ '&details=' + encodeURIComponent(res.message || '')
+ '&location=' + encodeURIComponent(res.event.location || '');

emailArgs.html = (res.message || '') +
'<br><br>' +
'<a href="' + gcalLink + '">📅 Add to Google Calendar</a>';
}

system.email(emailArgs);
call.say('We have emailed your details requested.');
}

// If there's a transfer request, process it after a delay
if (res.transfer) {
call.say(res.message);
setTimeout(function () {
call.transfer(res.destination);
}, 10000);
} else {
setTimeout(function () {
call.transfer('10');
}, 5000);
}
}
}

The external application, in response to the JSON data sent by the Vodia PBX, should return a JSON object with specific fields.

Email only — sends an email with no calendar attachment:

{ "email": "pbx@vodia.com", "message": "<p>Thanks Mr. Jones, your access code is 1234.</p>" }

Email + Calendar invite — sends an email with an .ics attachment and a Google Calendar link:

{
"email": "pbx@vodia.com",
"message": "<p>Thanks Mr. Jones, your appointment has been confirmed.</p>",
"event": {
"summary": "Callback with Company ABC",
"description": "Your scheduled support callback.",
"location": "Phone",
"start": "2026-03-20T09:00:00Z",
"end": "2026-03-20T09:30:00Z"
}
}

Transfer — reads the message aloud and transfers the call to the specified destination:

{ "destination": "785", "message": "Thanks Mr. Jones, we could not find your email, transferring you now.", "transfer": true }
tip

Use the 'Connection': 'keep-alive' header in your HTTP response from the external application.

note

The event.start and event.end fields must be in ISO 8601 UTC format (e.g. 2026-03-20T09:00:00Z). The .ics attachment triggers a native Accept / Decline / Tentative prompt in Outlook. Gmail users will see an "Add to Calendar" button automatically. The fallback Google Calendar link in the email body covers all other clients.

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