This tutorial will show you how to create a reminder system that will call you to remind you of upcoming meetings.
Ten minutes before each event in your calendar, your Switchvox PBX will call you to remind you that you have an upcoming appointment. If a phone number is found in your meeting description, you will have the option to press ‘1′ and have the Switchvox PBX call you back and connect you to that number at the meeting time. When your PBX calls you to connect you to that number, it will read to you any pin or extensions that it found in the appointment description before connecting you to the call.
For example, take the following scenario:
Let’s say I have the following appointment in iCal:

At 1:20 on that day, my phone will ring, and I’ll hear the following prompt:
“You have an appointment beginning in ten minutes. The following phone number was found in your appointment details: eight, five, eight, five, five, five, one, two, one, two. Press 1 if you would like the PBX it initiate a call to this number at the appointment time. Otherwise press 2.”
Since I know that the number I heard is the conference bridge I use with Joe and Fred, I press 1 so that I can automatically call in to the meeting.
Ten minutes later, my phone rings and I hear the following:
“The system found the following pin or extension in your appointment details: five, five, five, five. You will now be connected to you call.”
I then hear ringing on my phone, and am then connected to the conference bridge that Joe and Fred use. When prompted, I know to enter 5555 to join the correct conference room and Joe, Fred, and I begin our meeting.
This all sounds pretty complicated, but it’s actually quite simple to accomplish with some IVRs, the switchvox.call API, and just a small bit of coding. I will walk you through the whole process. This example is written in perl, but it shouldn’t be too difficult to write this using another language. This tutorial also assumes that the calendar application you are using stores or can export your appointments in iCalendar format. Most major applications do. A list of applications that support the iCalendar format can be found here.
- A perl script that runs on a cron to check if any calls need to be made.
- A mod_perl script running on Apache that accepts requests from the Switchvox PBX.
- A postgresql database with two tables for storing information about calls we need to make.
- Some IVRs on your Switchvox PBX.
That’s it!
| Sound Name | Description |
|---|---|
| Upcoming Appointment | “You have an upcoming appointment in 10 minutes.” |
| Upcoming Appointment: has number | “The following phone number was found in your appointment details.” |
| Upcoming Appointment: callback | “Press 1 if you would like the PBX to initiate a call to this number at the appointment time. Otherwise press 2.” |
| Upcoming Appointment: pin or ext | “The system found the following pin or extension in your appointment details.” |
| Upcoming Appointment: connecting | “You will now be connected to your call.” |

- The first action this IVR menu does is play the sound that tells you that you have an appointment in 10 minutes.
- It then has a conditional cause that checks an IVR variable called has_number. If this variable is not equal to 1, the caller will be redirected to the “Hangup” IVR menu because there is nothing more to do.
- Otherwise, the next action is to play the sound which informs the user that a phone number was found in the appointment details.
- It then uses the “Say Digits/Letters” action to read the phone number we found. This phone number is found in an IVR variable called callback_number.
- It then plays the sound that asks the user to press 1 if they would like their Switchvox to set up the call at the appointment time, or 2 if not.
- Finally, it listens for the user to select an option. If the caller presses 2, they are redirected to the “Hangup” IVR, and if they press 1, they are redirected to the “Appointment Setup callback” IVR menu.

- callback_number - The number to call
- callback_pin - Any pin or extension we found in the appointment details
- appointment_id - The ID of the appointment

- The first action is a Conditional Clause. If the has_number IVR variable is equal to 1, it sends the caller to the “Appointment remind pin” IVR Menu.
- Otherwise, it continues and plays the sound that informs the caller they are being connected to their call.
- Finally it sends the caller to an external number. The number to send the call to is in the callback_number IVR Variable. (Please note that depending upon your system setup, you may need to create a call rule to be used by this action to ensure your 10 digit number gets routed out to the correct provider on your system. Please see your Switchvox documentation on call rules for more details.)

- It plays a sound letting the caller know that a pin or extension was found along with the phone number in the appointment details.
- It then uses the Say Digits/Letters action to read the number back to the caller from the callback_pin IVR variable.
- It then redirects the caller to action two in the “Appointment callback” IVR menu.

CREATE TABLE appointment_callbacks (
appointment_id varchar(128),
callback_number varchar(16),
callback_pin varchar(6)
);
CREATE TABLE finished_reminders (
appointment_id varchar(128),
call_type varchar(16)
);
- iCal::Parser
- Switchvox::API
If you’re unfamiliar with how to get or install perl modules, http://www.cpan.org is a good place to start reading.
package SetCallback;
use strict;
use warnings;
use Apache2::RequestRec;
use Apache2::Const -compile => qw(OK);
use DBI;
use CGI;
use Data::Dumper;
sub handler {
my $apache = shift;
my $cgi = new CGI($apache->args);
my ($sql, $sth, $db);
$db = DBI->connect("dbi:Pg:dbname=appointments", "db_user", "db_pass");
my $insert_values = {};
$insert_values->{appointment_id} = $db->quote($cgi->param('appointment_id'));
$insert_values->{callback_number} = $db->quote($cgi->param('callback_number'));
$insert_values->{callback_pin} = $db->quote($cgi->param('callback_pin'));
if (!$insert_values->{callback_pin}) {
delete $insert_values->{callback_pin};
}
my $sql = "INSERT INTO appointment_callbacks (" . join(', ', keys %$insert_values) . ") ".
"VALUES (" . join(', ', values %$insert_values) . ")";
$sth = $db->prepare($sql);
$sth->execute();
return Apache2::Const::OK;
}
1;
#!/usr/bin/perl
BEGIN { $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; }
use Data::Dumper;
use DateTime;
use DBI;
use iCal::Parser;
use Switchvox::API;
my $ICAL_FILE = 'icsFiles/Appointments.ics';
my $EXTENSION = 100;
my $ACCOUNT_ID = 1102;
my $REMIND_IVR = 600;
my $CONNECT_IVR = 601;
my $SV_HOST = '10.10.2.202';
my $SV_ADMIN = 'admin';
my $SV_PASSWORD = 'password';
my $db;
#Get current date/time
my $now = DateTime->now();
my $day = $now->day();
my $month = $now->month();
my $year = $now->year();
my $now_epoch = $now->epoch();
#parse iCalendar file
my $parser = new iCal::Parser();
my $hash = $parser->parse($ICAL_FILE);
my $calendar = $parser->calendar();
my $todays_events = $calendar->{events}{$year}{$month}{$day};
# check all of todays events
foreach my $event_uid (keys %{$todays_events}) {
my $event = $todays_events->{$event_uid};
my $event_start_datetime = $event->{DTSTART};
# make sure event hasn't happened yet
if ($event_start_datetime->epoch() > $now_epoch) {
my $time_difference = $event_start_datetime->delta_ms($now);
if ($time_difference->{minutes} < 11 && $time_difference->{minutes} > 9) {
make_reminder_call(
event_uid => $event_uid,
db => $db,
event => $event
);
} elsif ($time_difference->{minutes} < 2) {
check_for_callback(
event_uid => $event_uid,
db => $db
);
}
}
}
# Initiate a call to remind user of appointment
sub make_reminder_call {
my %in = @_;
my ($sql, $sth, $callback_number, $callback_pin);
my $db = $in{db};
my $event_uid = $in{event_uid},
my $event = $in{event};
my $has_number = 0;
if (!$db) {
$db = DBI->connect("dbi:Pg:dbname=appointments", "ajp", "ajp");
}
# check if we've already made a call for this event
$sql = "SELECT COUNT(*) FROM finished_reminders ".
" WHERE call_type = 'remind' AND appointment_id = '$event_uid'";
$sth = $db->prepare($sql);
$sth->execute();
my $already_reminded = $sth->fetchrow_array;
return if $already_reminded;
if ($event->{DESCRIPTION} =~ m/\(?(\d{3})\)?[\s-.]?(\d{3})[\s-.]?(\d{4})/) {
$has_number = 1;
$callback_number = $1.$2.$3;
$event->{DESCRIPTION} =~ m/(\(?\d{3}\)?[\s-.]?\d{3}[\s-.]?\d{4})/;
my $whole_num = $1;
# see if we have a pin or ext in the description after the phone number
my $index_after_number = index($event->{DESCRIPTION}, $whole_num) + length($whole_num);
my $string_after_number = substr($event->{DESCRIPTION}, $index_after_number);
if ($string_after_number =~ m/(\d{3,6})/) {
$callback_pin = $1;
}
}
$sql = "INSERT INTO finished_reminders (call_type, appointment_id) ".
"VALUES ('remind', '$event_uid')";
$sth = $db->prepare($sql);
$sth->execute();
make_call(
has_number => $has_number,
callback_number => $callback_number,
callback_pin => $callback_pin,
appointment_id => $event_uid,
ivr => $REMIND_IVR
);
}
# Check if the user wants a call for this meeting, initiate callback if yes
sub check_for_callback {
my %in = @_;
my ($sql, $sth);
my $has_number = 0;
my $db = $in{db};
my $event_uid = $in{event_uid};
if (!$db) {
$db = DBI->connect("dbi:Pg:dbname=appointments", "ajp", "ajp");
}
# check if we're supposed to initiate the call for this event
$sql = "SELECT * FROM appointment_callbacks WHERE appointment_id = '$event_uid'";
$sth = $db->prepare($sql);
$sth->execute();
my $callback = $sth->fetchrow_hashref;
return if !$callback->{appointment_id};
# check if we've already made a call for this event
$sql = "SELECT COUNT(*) FROM finished_reminders ".
" WHERE call_type = 'callback' AND appointment_id = '$event_uid'";
$sth = $db->prepare($sql);
$sth->execute();
my $already_reminded = $sth->fetchrow_array;
return if $already_reminded;
if ($callback->{callback_pin}) {
$has_number = 1;
}
$sql = "INSERT INTO finished_reminders (call_type, appointment_id) ".
"VALUES ('callback', '$event_uid')";
$sth = $db->prepare($sql);
$sth->execute();
make_call(
has_number => $has_number,
callback_number => $callback->{callback_number},
callback_pin => $callback->{callback_pin},
appointment_id => $event_uid,
ivr => $CONNECT_IVR
);
}
# actually make the call
sub make_call {
my %in = @_;
my $has_number = $in{has_number};
my $callback_number = $in{callback_number};
my $callback_pin = $in{callback_pin};
my $appointment_id = $in{appointment_id};
my $ivr = $in{ivr};
my $api = new Switchvox::API(
hostname => $SV_HOST,
username => $SV_ADMIN,
password => $SV_PASSWORD
);
my $response = $api->api_request(
method => 'switchvox.call',
parameters => {
dial_first => $EXTENSION,
dial_as_account_id => $ACCOUNT_ID,
dial_second => $ivr,
variables => [ {
variable => [
'has_number=' . $has_number,
'callback_number=' . $callback_number,
'callback_pin=' . $callback_pin,
'appointment_id=' . $appointment_id
]
} ]
}
);
}
