Skip to content

Voicemail

An inbound application that handles answered inbound calls and offers the choice of recording a message or playing existing messages. An identity number is used to prevent unauthorised access to recorded messages. The recorded messages can be repeated, deleted or saved for later.

Uses actions: Run Menu, Play, Get Number, Redirect, Record, Delete File

{
    "actions" :
    [
        {
            "run_menu" :
            {
                "prompt" :
                {
                    "play" :
                    {
                        "play_list" :
                        [
                            {
                                "text_to_say" : "Press 1 to leave a message. Press 2 to listen to all messages."
                            }
                        ]
                    }
                },
                "menu_options" :
                [
                    {
                        "digit" : "1",
                        "next_page" :
                        {
                            "url" : "RecordMessage"
                        }
                    },
                    {
                        "digit" : "2",
                        "next_page" :
                        {
                            "url" : "GetPasswordNumber"
                        }
                    }
                ]
            }
        },
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Goodbye."
                    }
                ]
            }
        }
    ],
    "token" : "-1",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "get_number":
            {
                "prompt" :
                {
                    "play" :
                    {
                        "play_list" :
                        [
                            {
                                "text_to_say" : "Please enter your password number followed by the hash key."
                            }
                        ]
                    }
                },
                "end_digit" : "#",
                "next_page" :
                {
                    "url" : "CheckPasswordNumber"
                }
            }
        },
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Goodbye."
                    }
                ]
            }
        }
    ],
    "token" : "0",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "redirect" :
            {
                "next_page" :
                {
                    "url" : "PlayMessage"
                }
            }
        }
    ],
    "token" : "0",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Sorry, that password is not correct. Goodbye."
                    }
                ]
            }
        }
    ],
    "token" : "0",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "file_to_play" : "<message file name>"
                    }
                ]
            }
        },
        {
            "run_menu" :
            {
                "prompt" :
                {
                    "play" :
                    {
                        "play_list" :
                        [
                            {
                                "text_to_say" : "Press 1 to hear the message again. Press 2 to delete it, or press 3 to save it."
                            }
                        ]
                    }
                },
                "menu_options" :
                [
                    {
                        "digit" : "1",
                        "next_page" :
                        {
                            "url" : "MessageReplay"
                        }
                    },
                    {
                        "digit" : "2",
                        "next_page" :
                        {
                            "url" : "MessageDelete"
                        }
                    },
                    {
                        "digit" : "3",
                        "next_page" :
                        {
                            "url" : "MessageKeep"
                        }
                    }
                ]
            }
        },
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Goodbye."
                    }
                ]
            }
        }
    ],
    "token" : "0",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "End of messages. Goodbye."
                    }
                ]
            }
        }
    ],
    "token" : "",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Playing the message again."
                    }
                ]
            }
        },
        {
            "redirect" :
            {
                "next_page" :
                {
                    "url" : "PlayMessage"
                }
            }
        }
    ],
    "token" : "0",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "delete_file" :
            {
                "filename" : "/2014/08/12/12_30_01_04f01fb92e8913a8.65433.wav"
            }
        },
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Message deleted."
                    },
                    {
                        "text_to_say" : "Next message."
                    }
                ]
            }
        },
        {
            "redirect" :
            {
                "next_page" :
                {
                    "url" : "PlayMessage"
                }
            }
        }
    ],
    "token" : "1",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "delete_file" :
            {
                "filename" : "/2013/08/12/12_30_01_04f01fb92e8913a8.65433.wav"
            }
        },
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Message deleted."
                    }
                ]
            }
        },
        {
            "redirect" :
            {
                "next_page" :
                {
                    "url" : "PlayMessage"
                }
            }
        }
    ],
    "token" : "",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Message saved."
                    }
                ]
            }
        },
        {
            "play" :
            {
                "play_list":
                [
                    {
                        "text_to_say" : "Next message."
                    }
                ]
            }
        },
        {
            "redirect" :
            {
                "next_page" :
                {
                    "url" : "PlayMessage"
                }
            }
        }
    ],
    "token" : "1",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Message saved."
                    }
                ]
            }
        },
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "End of messages. Goodbye."
                    }
                ]
            }
        }
    ],
    "token" : "",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Please leave a message after the beep. Press hash to end the recording or press star to cancel."
                    }
                ]
            }
        },
        {
            "record" :
            {
                "beep_on_start" : true,
                "milliseconds_max_silence" : 5000,
                "barge_in_digits" : "#*",
                "next_page" :
                {
                    "url" : "RecordComplete"
                }
            }
        }
    ],
    "token" : "",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Thanks for leaving a message. Goodbye."
                    }
                ]
            }
        }
    ],
    "token" : "",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "Message recording cancelled. Goodbye."
                    }
                ]
            }
        },
        {
            "delete_file":
            {
                "filename": "/rest_api/recordings/2018/10/06/rest_16_39_23_058d242936dcc5f9.4460.wav"
            }
        }
    ],
    "token" : "",
    "api_version": "2.0"
}
{
    "actions" :
    [
        {
            "play" :
            {
                "play_list" :
                [
                    {
                        "text_to_say" : "There was no message recorded. Goodbye."
                    }
                ]
            }
        },
        {
            "delete_file":
            {
                "filename": "/rest_api/recordings/2018/10/06/rest_16_39_23_058d242936dcc5f9.4460.wav"
            }
        }
    ],
    "token" : "",
    "api_version": "2.0"
}
{
    [
    ],
    "token" : "Error for Action: xxxx  ActionIndex: xxxx  Result: xxxx",
    "api_version": "2.0"
}
{
    "api_version": "2.0"
}

Implementing this sample as an ASP.Net Web application:

// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class Voicemail : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>();

        // Create the menu action
        Play menuPrompt = Play.SayText("Press 1 to leave a message. " + 
                                       "Press 2 to listen to all messages.");
        List<MenuOption> menuOptions = new List<MenuOption>
        {
            new MenuOption('1', new WebPageRequest("RecordMessage.aspx")),
            new MenuOption('2', new WebPageRequest("GetPasswordNumber.aspx"))
        };
        RunMenu runMenuAction = new RunMenu(menuOptions, menuPrompt);
        actions.Add(runMenuAction);

        // And add a final message
        actions.Add(Play.SayText("Goodbye."));

        // Respond
        // Set the first message to play if there is one
        string messageId = "-1";
        MessageFilenameDatabase db = new MessageFilenameDatabase();
        var ids = db.GetIds();
        if (ids.Count > 0)
        {
            messageId = ids[0].ToString();
        }
        TelephonyResponse ourResponse = new TelephonyResponse(actions, messageId);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// GetPasswordNumber page

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class GetPasswordNumber : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        String token = ourRequest.InstanceInfo.Token;

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>();

        Play prompt = Play.SayText("Please enter your password number followed by the hash key.");
        GetNumber getNumberAction = new GetNumber(new WebPageRequest("CheckPasswordNumber.aspx"), prompt)
        {
            EndDigit = '#'
        };
        actions.Add(getNumberAction);

        actions.Add(Play.SayText("Goodbye."));

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// CheckPasswordNumber page

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class CheckPasswordNumber : System.Web.UI.Page
{
    private static String myPassword = "1234";

    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        String token = ourRequest.InstanceInfo.Token;
        GetNumberResult getNumberResult = (GetNumberResult)ourRequest.InstanceInfo.ActionResult;

        // Set up the actions, depending on the entered password
        List<TelephonyAction> actions = new List<TelephonyAction>();
        if (getNumberResult.EnteredNumber.Equals(myPassword))
        {
            actions.Add(new Redirect(new WebPageRequest("PlayMessage.aspx")));
        }
        else
        {
            actions.Add(Play.SayText("Sorry, that password is not correct. Goodbye."));
        }

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// PlayMessage

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class PlayMessage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        string token = ourRequest.InstanceInfo.Token;
        int messageId = Convert.ToInt32(token);

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>();

        MessageFilenameDatabase db = new MessageFilenameDatabase();
        string filename = db.GetMessageFilename(messageId);
        if (filename != null)
        {
            actions.Add(Play.PlayFile(filename));

            List<MenuOption> menuOptions = new List<MenuOption>
            {
                new MenuOption('1', new WebPageRequest("MessageReplay.aspx")),
                new MenuOption('2', new WebPageRequest("MessageDelete.aspx")),
                new MenuOption('3', new WebPageRequest("MessageKeep.aspx"))
            };
            actions.Add(new RunMenu(menuOptions, Play.SayText("Press 1 to hear the message again. " + 
                                                 "Press 2 to delete it, " + 
                                                 "or press 3 to save it.")
                                                 ));
            actions.Add(Play.SayText("Goodbye."));
        }
        else
        {
            actions.Add(Play.SayText("End of messages. Goodbye."));
            token = "";
        }

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// MessageReplay page

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class MessageReplay : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        String token = ourRequest.InstanceInfo.Token;

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>
        {
            Play.SayText("Playing the message again."),
            new Redirect(new WebPageRequest("PlayMessage.aspx"))
        };

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// MessageDelete page

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class MessageDelete : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        // The token should be the ID of the message file to delete
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        String token = ourRequest.InstanceInfo.Token;
        int messageId = Convert.ToInt32(token);

        MessageFilenameDatabase db = new MessageFilenameDatabase();
        String filename = db.GetMessageFilename(messageId);

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>();

        int nextMessageId = -1;
        if (filename != null)
        {
            // Delete action
            actions.Add(new DeleteFile(filename));
            Play msg = Play.SayText("Message deleted.");

            // Get the next message ID
            List<int> ids = db.GetIds();
            for (int index = 0; index < ids.Count - 1; index++)
            {
                if (ids[index] == messageId)
                {
                    nextMessageId = ids[index + 1];
                    msg.AddText("Next message.");
                    actions.Add(msg);
                    actions.Add(new Redirect(new WebPageRequest("PlayMessage.aspx")));
                    break;
                }
            }

            if (nextMessageId == -1)
            {
                msg.AddText("End of messages. Goodbye.");
                actions.Add(msg);
            }

            // Remove the entry from the DB
            db.DeleteMessageFilename(messageId);
        }

        // Respond
        token = nextMessageId.ToString();
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// MessageKeep page

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class MessageKeep : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        // The token should be the id of the message file to keep
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        String token = ourRequest.InstanceInfo.Token;
        int messageId = Convert.ToInt32(token);

        MessageFilenameDatabase db = new MessageFilenameDatabase();
        String filename = db.GetMessageFilename(messageId);

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>();
        int nextMessageId = -1;
        if (filename != null)
        {
            actions.Add(Play.SayText("Message saved."));

            // Get the next message ID
            List<int> ids = db.GetIds();
            for (int index = 0; index < ids.Count - 1; index++)
            {
                if (ids[index] == messageId)
                {
                    nextMessageId = ids[index + 1];
                    Play msg = Play.SayText("Next message.");
                    actions.Add(msg);
                    actions.Add(new Redirect(new WebPageRequest("PlayMessage.aspx")));
                    break;
                }
            }

            if (nextMessageId == -1)
            {
                actions.Add(Play.SayText("End of messages. Goodbye."));
            }
        }

        // Respond
        token = nextMessageId.ToString();
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// This prompts the caller to leave a message and starts a record action.

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class RecordMessage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        String token = ourRequest.InstanceInfo.Token;

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>
        {
            Play.SayText("Please leave a message after the beep. Press hash to end the " +
                         "recording or press star to cancel.")
        };

        Record recordAction = new Record(new WebPageRequest("RecordComplete.aspx"))
        {
            BeepOnStart = true,
            BargeInDigits = "#*",
            MillisecondsMaxSilence = 5000
        };
        actions.Add(recordAction);

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
// CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// RecordComplete page

using System;
using System.Collections.Generic;
using Aculab.Cloud.RestAPIWrapper;

public partial class RecordComplete : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        String token = ourRequest.InstanceInfo.Token;
        RecordResult result = (RecordResult)ourRequest.InstanceInfo.ActionResult;

        // Setup the actions
        List<TelephonyAction> actions = new List<TelephonyAction>();

        if (result.Reason.Equals("bargein") && result.BargeInDigit.Equals('*'))
        {
            actions.Add(Play.SayText("Message recording cancelled. Goodbye."));
            actions.Add(new DeleteFile(result.Filename));
        }
        else if (result.ContainsSound)
        {
            // Store the filename in our message file
            MessageFilenameDatabase db = new MessageFilenameDatabase();
            int newMessageId = db.AppendMessageFilename(result.Filename);
            actions.Add(Play.SayText("Thanks for leaving a message. Goodbye."));
        }
        else
        {
            actions.Add(Play.SayText("There was no message recorded. Goodbye."));
            actions.Add(new DeleteFile(result.Filename));
        }

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.ToHttpResponse(Response);
    }
}
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

// File DB of voicemail message filenames
//
// Each row:  int Id, string Filename
//
public class MessageFilenameDatabase
{
    private int nextId = 1;

    public MessageFilenameDatabase()
    {
        DbFilename = Path.GetTempPath() + "CSharpVoicemailMessages.tmp";

        // Initialise the id counter to the next highest int
        Dictionary<int, string> rows = GetRows();
        if (rows.Count > 0)
        {
            nextId = (new List<int>(rows.Keys)).Max() + 1;
        }
    }

    public int Size()
    {
        if (!File.Exists(DbFilename))
        {
            return 0;
        }
        String[] messageLines = File.ReadAllLines(DbFilename);
        return messageLines.Length;
    }

    public List<int> GetIds()
    {
        if (!File.Exists(DbFilename))
        {
            return new List<int>();
        }

        Dictionary<int, string> rows = GetRows();
        return new List<int>(rows.Keys);
    }

    public String GetMessageFilename(int id)
    {
        if (!File.Exists(DbFilename))
        {
            return null;
        }

        Dictionary<int, string> rows = GetRows();
        try
        {
            string filename = rows[id];
            return filename;
        }
        catch (KeyNotFoundException)
        {
            return null;
        }
    }

    public int AppendMessageFilename(String filename)
    {
        Dictionary<int, string> rows;
        if (File.Exists(DbFilename))
        {
            rows = GetRows();
        }
        else
        {
            rows = new Dictionary<int, string>();
        }

        int newId = nextId++;
        rows.Add(newId, filename);
        SaveDatabaseFile(rows);

        return newId;
    }

    public void DeleteMessageFilename(int id)
    {
        if (!File.Exists(DbFilename))
        {
            return;
        }

        Dictionary<int, string> rows = GetRows();
        try
        {
            rows.Remove(id);
        }
        catch (KeyNotFoundException)
        {
            return;
        }

        SaveDatabaseFile(rows);
    }

    private void SaveDatabaseFile(Dictionary<int, String> rows)
    {
        List<string> lines = new List<string>(rows.Count);
        foreach (KeyValuePair<int, string> entry in rows)
        {
            lines.Add(String.Format("{0},{1}", entry.Key, entry.Value));
        }
        File.WriteAllLines(DbFilename, lines.ToArray());
    }

    public String DbFilename
    {
        get
        {
            return dbFilename;
        }
        private set
        {
            dbFilename = value;
        }
    }

    public Dictionary<int, string> GetRows()
    {
        Dictionary<int, string> rows = new Dictionary<int, string>();

        if (File.Exists(DbFilename))
        {
            String[] messageLines = File.ReadAllLines(DbFilename);
            foreach (string line in messageLines)
            {
                string[] splits = line.Split(',');
                int id = Int32.Parse(splits[0]);
                string filename = splits[1].Trim();
                rows.Add(id, filename);
            }
        }
        return rows;
    }

    private String dbFilename;
}
using System;
using Aculab.Cloud.RestAPIWrapper;

public partial class ErrorPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
        ErrorResult result = ourRequest.InstanceInfo.ErrorResult;

        String token = String.Format("Action: {0}\nActionIndex: {1}\nResult: {2}",
            result.Action, result.ActionIndex, result.Result);

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(null, token);
        ourResponse.ToHttpResponse(Response);
    }
}
using System;
using Aculab.Cloud.RestAPIWrapper;

public partial class FinalPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(Request);
    }
}

Implementing this sample as an ASP.Net Core Web application:

// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    [Route("Voicemail")]
    public class VoicemailController : ControllerBase
    {
        private static readonly String myPassword = "1234";
        // Process the GET or POST request, set up the actions and construct the json response.
        [Route("FirstPage")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> Voicemail()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);

                // Setup the actions required
                List<TelephonyAction> actions = new List<TelephonyAction>();

                // Create the menu action
                Play menuPrompt = Play.SayText("Press 1 to leave a message. " +
                                               "Press 2 to listen to all messages.");
                List<MenuOption> menuOptions = new List<MenuOption>()
                {
                    new MenuOption('1', new WebPageRequest("Voicemail/RecordMessage")),
                    new MenuOption('2', new WebPageRequest("Voicemail/GetPasswordNumber"))
                };
                RunMenu runMenuAction = new RunMenu(menuOptions, menuPrompt);
                actions.Add(runMenuAction);

                // And add a final message
                actions.Add(Play.SayText("Goodbye."));

                // Create response
                // Set the first message to play if there is one
                string messageId = "-1";
                MessageFilenameDatabase db = new MessageFilenameDatabase();
                var ids = db.GetIds();
                if (ids.Count > 0)
                {
                    messageId = ids[0].ToString();
                }
                TelephonyResponse ourResponse = new TelephonyResponse(actions, messageId);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    [Route("Voicemail")]
    public class VoicemailController : ControllerBase
    {
        private static readonly String myPassword = "1234";
        // Process the GET or POST request, extract the transcription results and write to a file
        [Route("GetPasswordNumber")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> GetPasswordNumber()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;

                // Setup the actions required
                List<TelephonyAction> actions = new List<TelephonyAction>();

                var prompt = Play.SayText("Please enter your password number followed by the hash key.");
                var nextPage = new WebPageRequest("Voicemail/CheckPasswordNumber");
                var getNumberAction = new GetNumber(nextPage, prompt)
                {
                    EndDigit = '#'
                };
                actions.Add(getNumberAction);

                actions.Add(Play.SayText("Goodbye."));

                // Create response
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    [Route("Voicemail")]
    public class VoicemailController : ControllerBase
    {
        private static readonly String myPassword = "1234";
        // Process the GET or POST request, set up the actions and construct the json response.
        [Route("CheckPasswordNumber")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> CheckPasswordNumber()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;
                GetNumberResult getNumberResult = (GetNumberResult)telephonyRequest.InstanceInfo.ActionResult;

                // Set up the actions, depending on the entered password
                List<TelephonyAction> actions = new List<TelephonyAction>();
                if (getNumberResult.EnteredNumber.Equals(myPassword))
                {
                    actions.Add(new Redirect(new WebPageRequest("Voicemail/PlayMessage")));
                }
                else
                {
                    actions.Add(Play.SayText("Sorry, that password is not correct. Goodbye."));
                }

                // Create response
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    [Route("Voicemail")]
    public class VoicemailController : ControllerBase
    {
        private static readonly String myPassword = "1234";
        [Route("PlayMessage")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> PlayMessage()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                string token = telephonyRequest.InstanceInfo.Token;
                int messageId = Convert.ToInt32(token);

                // Setup the actions required
                List<TelephonyAction> actions = new List<TelephonyAction>();

                MessageFilenameDatabase db = new MessageFilenameDatabase();
                string filename = db.GetMessageFilename(messageId);
                if (filename != null)
                {
                    actions.Add(Play.PlayFile(filename));

                    List<MenuOption> menuOptions = new List<MenuOption>
                    {
                        new MenuOption('1', new WebPageRequest("Voicemail/MessageReplay")),
                        new MenuOption('2', new WebPageRequest("Voicemail/MessageDelete")),
                        new MenuOption('3', new WebPageRequest("Voicemail/MessageKeep"))
                    };
                    actions.Add(new RunMenu(menuOptions, Play.SayText("Press 1 to hear the message again. " +
                                                                      "Press 2 to delete it, " +
                                                                      "or press 3 to save it.")));
                    actions.Add(Play.SayText("Goodbye."));
                }
                else
                {
                    actions.Add(Play.SayText("End of messages. Goodbye."));
                    token = "";
                }

                // Create response
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    [Route("Voicemail")]
    public class VoicemailController : ControllerBase
    {
        [Route("MessageReplay")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> MessageReplay()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;

                // Setup the actions required
                List<TelephonyAction> actions = new List<TelephonyAction>()
                {
                    Play.SayText("Playing the message again."),
                    new Redirect(new WebPageRequest("Voicemail/PlayMessage"))
                };
                // Create response
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
            catch (Exception e)
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    [Route("Voicemail")]
    public class VoicemailController : ControllerBase
        [Route("MessageDelete")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> MessageDelete()
        {
            try
            {
                // Unpack the request
                // The token should be the ID of the message file to delete
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;
                int messageId = Convert.ToInt32(token);

                var db = new MessageFilenameDatabase();
                String filename = db.GetMessageFilename(messageId);

                // Setup the actions required
                List<TelephonyAction> actions = new List<TelephonyAction>();

                int nextMessageId = -1;
                if (filename != null)
                {
                    // Delete action
                    actions.Add(new DeleteFile(filename));
                    Play msg = Play.SayText("Message deleted.");

                    // Get the next message ID
                    List<int> ids = db.GetIds();
                    for (int index = 0; index < ids.Count - 1; index++)
                    {
                        if (ids[index] == messageId)
                        {
                            nextMessageId = ids[index + 1];
                            msg.AddText("Next message.");
                            actions.Add(msg);
                            actions.Add(new Redirect(new WebPageRequest("Voicemail/PlayMessage")));
                            break;
                        }
                    }

                    if (nextMessageId == -1)
                    {
                        msg.AddText("End of messages. Goodbye.");
                        actions.Add(msg);
                    }

                    // Remove the entry from the DB
                    db.DeleteMessageFilename(messageId);
                }

                // Create response
                token = nextMessageId.ToString();
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
            catch (Exception e)
            {
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    [Route("Voicemail")]
        [Route("MessageKeep")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> MessageKeep()
        {
            try
            {
                // Unpack the request
                // The token should be the id of the message file to keep
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;
                int messageId = Convert.ToInt32(token);

                MessageFilenameDatabase db = new MessageFilenameDatabase();
                String filename = db.GetMessageFilename(messageId);

                // Setup the actions
                List<TelephonyAction> actions = new List<TelephonyAction>();
                int nextMessageId = -1;
                if (filename != null)
                {
                    actions.Add(Play.SayText("Message saved."));

                    // Get the next message ID
                    List<int> ids = db.GetIds();
                    for (int index = 0; index < ids.Count - 1; index++)
                    {
                        if (ids[index] == messageId)
                        {
                            nextMessageId = ids[index + 1];
                            Play msg = Play.SayText("Next message.");
                            actions.Add(msg);
                            actions.Add(new Redirect(new WebPageRequest("Voicemail/PlayMessage")));
                            break;
                        }
                    }

                    if (nextMessageId == -1)
                    {
                        actions.Add(Play.SayText("End of messages. Goodbye."));
                    }
                }

                // Create response
                token = nextMessageId.ToString();
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
            catch (Exception e)
            {
                return StatusCode((int)HttpStatusCode.InternalServerError, e.Message);
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
        [Route("RecordMessage")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> RecordMessage()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;

                // Setup the actions required
                List<TelephonyAction> actions = new List<TelephonyAction>()
                {
                    Play.SayText("Please leave a message after the beep. Press hash to end the " +
                                         "recording or press star to cancel.")
                };

                Record recordAction = new Record(new WebPageRequest("Voicemail/RecordComplete"))
                {
                    BeepOnStart = true,
                    BargeInDigits = "#*",
                    MillisecondsMaxSilence = 5000
                };
                actions.Add(recordAction);

                // Create response
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
            catch (Exception e)
            {
                return StatusCode((int)HttpStatusCode.InternalServerError, e.Message);
            }
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.
//
// Voicemail:
// An application that responds to inbound calls by prompting either
// to leave a message or listen to all messages.
// Recorded messages are saved to a simple file database.
// Listening to messages requires a numeric password to be entered.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
        [Route("RecordComplete")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> RecordComplete()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;
                RecordResult result = (RecordResult)telephonyRequest.InstanceInfo.ActionResult;

                // Setup the actions required
                List<TelephonyAction> actions = new List<TelephonyAction>();

                if (result.Reason.Equals("bargein") && result.BargeInDigit.Equals('*'))
                {
                    actions.Add(Play.SayText("Message recording cancelled. Goodbye."));
                    actions.Add(new DeleteFile(result.Filename));
                }
                else if (result.ContainsSound)
                {
                    // Store the filename in our message file
                    MessageFilenameDatabase db = new MessageFilenameDatabase();
                    int newMessageId = db.AppendMessageFilename(result.Filename);
                    actions.Add(Play.SayText("Thanks for leaving a message. Goodbye."));
                }
                else
                {
                    actions.Add(Play.SayText("There was no message recorded. Goodbye."));
                    actions.Add(new DeleteFile(result.Filename));
                }

                // Create response
                TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
            catch (Exception e)
            {
                return StatusCode((int)HttpStatusCode.InternalServerError, e.Message);
            }
        }
    }
}
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples
{
    // File DB of voicemail message filenames
    //
    // Each row:  int Id, string Filename
    //
    public class MessageFilenameDatabase
    {
        private int nextId = 1;

        public MessageFilenameDatabase()
        {
            DbFilename = Path.GetTempPath() + "CSharpNetCoreVoicemailMessages.tmp";

            // Initialise the id counter to the next highest int
            Dictionary<int, string> rows = GetRows();
            if (rows.Count > 0)
            {
                nextId = (new List<int>(rows.Keys)).Max() + 1;
            }
        }

        public int Size()
        {
            if (!File.Exists(DbFilename))
            {
                return 0;
            }
            String[] messageLines = File.ReadAllLines(DbFilename);
            return messageLines.Length;
        }

        public List<int> GetIds()
        {
            if (!File.Exists(DbFilename))
            {
                return new List<int>();
            }

            Dictionary<int, string> rows = GetRows();
            return new List<int>(rows.Keys);
        }

        public String GetMessageFilename(int id)
        {
            if (!File.Exists(DbFilename))
            {
                return null;
            }

            Dictionary<int, string> rows = GetRows();
            try
            {
                string filename = rows[id];
                return filename;
            }
            catch (KeyNotFoundException)
            {
                return null;
            }
        }

        public int AppendMessageFilename(String filename)
        {
            Dictionary<int, string> rows;
            if (File.Exists(DbFilename))
            {
                rows = GetRows();
            }
            else
            {
                rows = new Dictionary<int, string>();
            }

            int newId = nextId++;
            rows.Add(newId, filename);
            SaveDatabaseFile(rows);

            return newId;
        }

        public void DeleteMessageFilename(int id)
        {
            if (!File.Exists(DbFilename))
            {
                return;
            }

            Dictionary<int, string> rows = GetRows();
            try
            {
                rows.Remove(id);
            }
            catch (KeyNotFoundException)
            {
                return;
            }

            SaveDatabaseFile(rows);
        }

        private void SaveDatabaseFile(Dictionary<int, String> rows)
        {
            List<string> lines = new List<string>(rows.Count);
            foreach (KeyValuePair<int, string> entry in rows)
            {
                lines.Add(String.Format("{0},{1}", entry.Key, entry.Value));
            }
            File.WriteAllLines(DbFilename, lines);
        }

        public String DbFilename
        {
            get
            {
                return dbFilename;
            }
            private set
            {
                dbFilename = value;
            }
        }

        public Dictionary<int, string> GetRows()
        {
            Dictionary<int, string> rows = new Dictionary<int, string>();

            if (File.Exists(DbFilename))
            {
                String[] messageLines = File.ReadAllLines(DbFilename);
                foreach (string line in messageLines)
                {
                    string[] splits = line.Split(',');
                    int id = Int32.Parse(splits[0]);
                    string filename = splits[1].Trim();
                    rows.Add(id, filename);
                }
            }
            return rows;
        }

        private String dbFilename;
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.

using System;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    public class RESTSampleController : ControllerBase
    {
        // Process the GET or POST request for the Error condition
        [Route("ErrorPage")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> ErrorPage()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                ErrorResult result = telephonyRequest.InstanceInfo.ErrorResult;

                String token = String.Format("Action: {0}\nActionIndex: {1}\nResult: {2}",
                    result.Action, result.ActionIndex, result.Result);

                // Create response
                TelephonyResponse ourResponse = new TelephonyResponse(null, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
            catch (Exception e)
            {
                return StatusCode((int)HttpStatusCode.InternalServerError, e.Message);
            }
        }
    }
}
// ASP.NET Core CSharp Wrapper sample for the Aculab Telephony REST API.

using System;
using Microsoft.AspNetCore.Mvc;
using Aculab.Cloud.RestAPIWrapper;
using System.Net;
using System.Threading.Tasks;

namespace Aculab.Cloud.RESTAPI.NETCoreCSharpSamples.Controllers
{
    public class RESTSampleController : ControllerBase
    {
        // Process the GET or POST request for the Final Page
        [Route("FinalPage")]
        [HttpGet]
        [HttpPost]
        [ProducesResponseType(200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> FinalPage()
        {
            try
            {
                // Unpack the request
                var telephonyRequest = await TelephonyRequest.UnpackRequestAsync(Request);
                String token = telephonyRequest.InstanceInfo.Token;

                // Create response
                // Only very limited actions can be returned here
                TelephonyResponse ourResponse = new TelephonyResponse(null, token);
                return new OkObjectResult(ourResponse.ToJson(this));
            }
            catch (ArgumentException)
            {
                return BadRequest();
            }
            catch (Exception e)
            {
                return StatusCode((int)HttpStatusCode.InternalServerError, e.Message);
            }
        }
    }
}

Implemented as ASP.Net Web Forms:

' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' The first page for the Voicemail sample:
' An application that responds to inbound calls by prompting either
' to leave a message or listen to all messages.
' Recorded messages are saved to a simple file database.
' Listening to messages requires a numeric password to be entered.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class Voicemail
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)

        ' Create the menu action
        Dim menuPrompt As Play = Play.SayText("Press 1 to leave a message. " +
                                              "Press 2 to listen to all messages.")

        Dim menuOptions As List(Of MenuOption) = New List(Of MenuOption) From {
            New MenuOption("1", New WebPageRequest("RecordMessage.aspx")),
            New MenuOption("2", New WebPageRequest("GetPasswordNumber.aspx"))
        }
        Dim runMenuAction As RunMenu = New RunMenu(menuOptions, menuPrompt)
        actions.Add(runMenuAction)

        ' And add a final message
        actions.Add(Play.SayText("Goodbye."))

        ' Set the first message to play if there Is one
        Dim messageId As String = "-1"
        Dim db As MessageFilenameDatabase = New MessageFilenameDatabase()
        Dim ids = db.GetIds()
        If (ids.Count > 0) Then
            messageId = ids(0).ToString()
        End If
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, messageId)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Voicemail sample:
' This prompts the caller for a password number.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class GetPasswordNumber
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)
        Dim token As String = ourRequest.InstanceInfo.Token

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)

        Dim prompt As Play = Play.SayText("Please enter your password number followed by the hash key.")
        Dim getNumberAction As GetNumber = New GetNumber(New WebPageRequest("CheckPasswordNumber.aspx"), prompt)
        getNumberAction.EndDigit = "#"
        getNumberAction.NextPage = New WebPageRequest("CheckPasswordNumber.aspx")
        actions.Add(getNumberAction)

        actions.Add(Play.SayText("Goodbye."))

        ' Respond
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Voicemail sample:
' This checks the password that has been entered.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class CheckPasswordNumber
    Inherits System.Web.UI.Page

    Shared myPassword As String = "1234"

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)

        Dim token As String = ourRequest.InstanceInfo.Token
        Dim result As GetNumberResult = ourRequest.InstanceInfo.ActionResult

        ' Set up the actions, depending on the entered password
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)
        If result.EnteredNumber.CompareTo(myPassword) = 0 Then
            actions.Add(New Redirect(New WebPageRequest("PlayMessage.aspx")))
        Else
            actions.Add(Play.SayText("Sorry, that password is not correct. Goodbye."))
        End If

        ' Respond
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Voicemail sample:
' This plays the message then prompts the caller for a further option.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class PlayMessage
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)
        Dim token As String = ourRequest.InstanceInfo.Token
        Dim messageId As Integer = Convert.ToInt32(token)

        Dim db As MessageFilenameDatabase = New MessageFilenameDatabase()
        Dim filename As String = db.GetMessageFilename(messageId)

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)
        If filename IsNot Nothing Then
            actions.Add(Play.PlayFile(filename))

            Dim menuOptions As List(Of MenuOption) = New List(Of MenuOption)
            menuOptions.Add(New MenuOption("1", New WebPageRequest("MessageReplay.aspx")))
            menuOptions.Add(New MenuOption("2", New WebPageRequest("MessageDelete.aspx")))
            menuOptions.Add(New MenuOption("3", New WebPageRequest("MessageKeep.aspx")))
            Dim prompt As Play = Play.SayText("Press 1 to hear the message again. " +
                                              "Press 2 to delete it, " +
                                              "or press 3 to save it.")
            actions.Add(New RunMenu(menuOptions, prompt))
            actions.Add(Play.SayText("Goodbye."))
        Else
            actions.Add(Play.SayText("End of messages. Goodbye."))
            token = ""
        End If

        ' Respond
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Voicemail sample:
' This replays the selected message.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class MessageReplay
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)
        Dim token As String = ourRequest.InstanceInfo.Token

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)
        actions.Add(Play.SayText("Playing the message again."))
        actions.Add(New Redirect(New WebPageRequest("PlayMessage.aspx")))

        ' Respond
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Simple Play With Interrupt sample:
' This acknowledges the interrupt to play by saying some text
' before the call is hung up.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class MessageDelete
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        ' The token should be the index of the message file to delete
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)

        Dim token As String = ourRequest.InstanceInfo.Token
        Dim messageId As Integer = -1
        messageId = Convert.ToInt32(token)

        Dim db As MessageFilenameDatabase = New MessageFilenameDatabase()
        Dim filename As String = db.GetMessageFilename(messageId)

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)

        Dim nextMessageId As Integer = -1
        If filename IsNot Nothing Then
            ' Delete action
            actions.Add(New DeleteFile(filename))
            Dim msg As Play = Play.SayText("Message deleted.")

            ' Get the next message ID
            Dim ids As List(Of Integer) = db.GetIds()
            For index As Integer = 0 To (ids.Count - 2)
                If ids(index) = messageId Then
                    nextMessageId = ids(index + 1)
                    msg.AddText("Next message.")
                    actions.Add(msg)
                    actions.Add(New Redirect(New WebPageRequest("PlayMessage.aspx")))
                    Exit For
                End If
            Next

            If nextMessageId = -1 Then
                msg.AddText("End of messages. Goodbye.")
                actions.Add(msg)
            End If

            ' Remove the entry from the DB
            db.DeleteMessageFilename(messageId)
        End If

        ' Respond
        token = nextMessageId.ToString()
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Voicemail sample:
' This saves the selected message.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class MessageKeep
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        ' The token should be the id of the message file to keep
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)
        Dim token As String = ourRequest.InstanceInfo.Token
        Dim messageId As Integer = Convert.ToInt32(token)

        Dim db As MessageFilenameDatabase = New MessageFilenameDatabase()
        Dim filename As String = db.GetMessageFilename(messageId)

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)
        Dim nextMessageId As Integer = -1
        If filename IsNot Nothing Then
            actions.Add(Play.SayText("Message saved."))

            ' Get the next message ID
            Dim ids As List(Of Integer) = db.GetIds()
            For index As Integer = 0 To (ids.Count - 2)
                If ids(index) = messageId Then
                    nextMessageId = ids(index + 1)
                    Dim msg As Play = Play.SayText("Next message.")
                    actions.Add(msg)
                    actions.Add(New Redirect(New WebPageRequest("PlayMessage.aspx")))
                    Exit For
                End If
            Next

            If nextMessageId = -1 Then
                actions.Add(Play.SayText("End of messages. Goodbye."))
            End If
        End If

        ' Respond
        token = nextMessageId.ToString()
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Voicemail sample:
' This prompts the caller to leave a message and starts a record action.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class RecordMessage
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)
        Dim token As String = ourRequest.InstanceInfo.Token

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction) From
        {
            Play.SayText("Please leave a message after the beep. Press hash to end the " +
                         "recording or press star to cancel.")
        }

        Dim recordAction As Record = New Record(New WebPageRequest("RecordComplete.aspx")) With {
            .BeepOnStart = True,
            .BargeInDigits = "#*",
            .MillisecondsMaxSilence = 5000
        }
        actions.Add(recordAction)

        ' Respond
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A page from the Voicemail sample:
' This is called when recording completes. It plays one of two messages to
' the caller depending on whether the recording contains significant sound.
Imports System.Collections.Generic
Imports Aculab.Cloud.RestAPIWrapper

Partial Class RecordComplete
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)
        Dim token As String = ourRequest.InstanceInfo.Token
        Dim result As RecordResult = ourRequest.InstanceInfo.ActionResult

        ' Setup the actions
        Dim actions As List(Of TelephonyAction) = New List(Of TelephonyAction)

        Dim helpDigit As Char = "*"
        If result.Reason.Equals("bargein") And result.BargeInDigit.Equals(helpDigit) Then
            actions.Add(Play.SayText("Message recording cancelled. Goodbye."))
            actions.Add(New DeleteFile(result.Filename))
        ElseIf result.ContainsSound Then
            ' Store the filename in our message file
            Dim db As MessageFilenameDatabase = New MessageFilenameDatabase()
            Dim newMessageId As Integer = db.AppendMessageFilename(result.Filename)
            actions.Add(Play.SayText("Thanks for leaving a message. Goodbye."))
        Else
            actions.Add(Play.SayText("There was no message recorded. Goodbye."))
            actions.Add(New DeleteFile(result.Filename))
        End If

        ' Respond
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(actions, token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
Imports System.IO
Imports System.Collections.Generic

' File DB of voicemail message filenames
'
' Each row:  int Id, string Filename
'
Public Class MessageFilenameDatabase

    Private nextId As Integer = 1

    Public Sub New()
        dbFilename = Path.GetTempPath() + "VBVoicemailMessages.tmp"

        ' Initialise the id counter to the next highest int
        Dim rows As Dictionary(Of Integer, String) = GetRows()
        If rows.Count > 0 Then
            nextId = (New List(Of Integer)(rows.Keys)).Max() + 1
        End If
    End Sub

    Public Function Size() As Integer
        If Not File.Exists(dbFilename) Then
            Return 0
        End If

        Dim lines As Array = File.ReadAllLines(dbFilename)
        Return lines.Length
    End Function

    Public Function GetIds() As List(Of Integer)
        If Not File.Exists(dbFilename) Then
            Return New List(Of Integer)
        End If

        Dim rows As Dictionary(Of Integer, String) = GetRows()
        Return New List(of integer)(rows.Keys)
    End Function

    Public Function GetMessageFilename(id As Integer) As String
        If Not File.Exists(dbFilename) Then
            Return Nothing
        End If

        Dim rows As Dictionary(Of Integer, String) = GetRows()
        Try
            Return rows(id)
        Catch ex As KeyNotFoundException
            Return Nothing
        End Try
    End Function

    Public Function AppendMessageFilename(filename As String) As Integer
        Dim rows As Dictionary(Of Integer, String)
        If File.Exists(dbFilename) Then
            rows = GetRows()
        Else
            rows = New Dictionary(Of Integer, String)
        End If

        Dim newId As Integer = nextId
        nextId += 1
        rows.Add(newId, filename)
        SaveDatabaseFile(rows)
        Return nextId
    End Function

    Public Sub DeleteMessageFilename(id As Integer)
        If Not File.Exists(dbFilename) Then
            Return
        End If

        Dim rows As Dictionary(Of Integer, String) = GetRows()
        Try
            rows.Remove(id)
        Catch ex As KeyNotFoundException
            Return
        End Try

        SaveDatabaseFile(rows)
    End Sub

    Private Sub SaveDatabaseFile(rows As Dictionary(Of Integer, String))
        Dim lines As List(Of String) = New List(Of String)(rows.Count)
        For Each entry As KeyValuePair(Of Integer, String) In rows
            lines.Add(String.Format("{0},{1}", entry.Key, entry.Value))
        Next
        File.WriteAllLines(dbFilename, lines)
    End Sub

    Public Function GetRows() As Dictionary(Of Integer, String)
        Dim rows As Dictionary(Of Integer, String) = New Dictionary(Of Integer, String)

        If File.Exists(dbFilename) Then
            Dim messageLines As String() = File.ReadAllLines(dbFilename)
            For Each line As String In messageLines
                Dim splits As String() = line.Split(",")
                Dim id As Integer = Int32.Parse(splits(0))
                Dim filename As String = splits(1).Trim()
                rows.Add(id, filename)
            Next
        End If
        Return rows
    End Function

    Private dbFilename As String
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A generic error page for all the samples.
Imports Aculab.Cloud.RestAPIWrapper

Partial Class ErrorPage
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)
        Dim result As ErrorResult = ourRequest.InstanceInfo.ErrorResult

        Dim token As String = String.Format("Action: {0}\nActionIndex: {1}\nResult: {2}", _
            result.Action, result.ActionIndex, result.Result)

        ' Respond
        Dim ourResponse As TelephonyResponse = New TelephonyResponse(token)
        ourResponse.ToHttpResponse(Response)
    End Sub
End Class
' Visual Basic Wrapper sample for the Aculab Telephony REST API.
'
' A generic final page for all the samples:
Imports Aculab.Cloud.RestAPIWrapper

Partial Class FinalPage
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

        ' Unpack the request
        Dim ourRequest As TelephonyRequest = New TelephonyRequest(Request)

        ' Do application tidying up
        ' ...
    End Sub
End Class

Implemented as Java Servlets:

// Java Servlet sample for the Aculab Telephony REST API.
//
// Voicemail:

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class Voicemail extends HttpServlet
{
    private static final long serialVersionUID = -216367273425731462L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        // Set up the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();

        // Create the menu action
        Play menuPrompt = Play.sayText("Press 1 to leave a message. " + 
                                       "Press 2 to listen to all messages.");
        List<MenuOption> menuOptions = new ArrayList<MenuOption>();
        menuOptions.add(new MenuOption('1', new WebPageRequest("RecordMessage")));
        menuOptions.add(new MenuOption('2', new WebPageRequest("GetPasswordNumber")));
        RunMenu runMenuAction = new RunMenu(menuPrompt, menuOptions);
        actions.add(runMenuAction);

        // And add a final message
        actions.add(Play.sayText("Goodbye."));

        //Get the first message id in the filename database
        MessageFilenameDatabase db = new MessageFilenameDatabase();
        String token = Integer.toString(db.getFirstMessageId());

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class GetPasswordNumber extends HttpServlet
{
    private static final long serialVersionUID = -7217763794925626580L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        String token = ourRequest.getInstanceInfo().getToken();

        // Set up the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();

        Play prompt = Play.sayText("Please enter your password number followed by the hash key.");
        GetNumber getNumberAction = new GetNumber(new WebPageRequest("CheckPasswordNumber"));
        getNumberAction.setPrompt(prompt);
        getNumberAction.setEndDigit('#');
        actions.add(getNumberAction);

        actions.add(Play.sayText("Goodbye."));

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class CheckPasswordNumber extends HttpServlet
{
    private static final long serialVersionUID = 2185118085479932134L;

    private static String myPassword = "1234";

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        String token = ourRequest.getInstanceInfo().getToken();
        GetNumberResult getNumberResult = (GetNumberResult)ourRequest.getInstanceInfo().getActionResult();

        // Set up the actions, depending on the entered password
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();
        if (getNumberResult.getEnteredNumber().compareTo(myPassword) == 0)
        {
            actions.add(new Redirect(new WebPageRequest("PlayMessage")));
        }
        else
        {
            actions.add(Play.sayText("Sorry, that password is not correct. Goodbye."));
        }

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class PlayMessage extends HttpServlet
{
    private static final long serialVersionUID = 6018322048224034139L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        // The token should be the index of the next message file to play
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        // Set up the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();
        MessageFilenameDatabase db = new MessageFilenameDatabase();
        String token = ourRequest.getInstanceInfo().getToken();

        int messageId = Integer.parseInt(token);

        String filename = db.getMessageFilename(messageId);
        if (filename != null)
        {
            actions.add(Play.playFile(filename));

            List<MenuOption> menuOptions = new ArrayList<MenuOption>();
            menuOptions.add(new MenuOption('1', new WebPageRequest("MessageReplay")));
            menuOptions.add(new MenuOption('2', new WebPageRequest("MessageDelete")));
            menuOptions.add(new MenuOption('3', new WebPageRequest("MessageKeep")));
            actions.add(new RunMenu(Play.sayText("Press 1 to hear the message again. " +
                                                 "Press 2 to delete it, " +
                                                 "or press 3 to save it."),
                                                 menuOptions));
            actions.add(Play.sayText("Goodbye."));

            token = Integer.toString(messageId);
        }
        else
        {
            actions.add(Play.sayText("End of messages. Goodbye."));
            token = "";
        }

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class MessageReplay extends HttpServlet
{
    private static final long serialVersionUID = -6227827216303408130L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        String token = ourRequest.getInstanceInfo().getToken();

        // Set up the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();
        actions.add(Play.sayText("Playing the message again."));
        actions.add(new Redirect(new WebPageRequest("PlayMessage")));

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class MessageDelete extends HttpServlet
{
    private static final long serialVersionUID = 5011613038036857392L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        // The token should be the index of the message file to delete
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        String token = ourRequest.getInstanceInfo().getToken();
        int messageId = Integer.parseInt(token);
        MessageFilenameDatabase db = new MessageFilenameDatabase();
        String filename = db.getMessageFilename(messageId);

        // Set up the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();
        int nextMessageId = -1;
        if (filename != null)
        {
            actions.add(new DeleteFile(filename));
            Play msg = Play.sayText("Message deleted.");

            nextMessageId = db.getNextMessageId(messageId);

            if (nextMessageId != -1)
            {
                msg.addText("Next message.");
                actions.add(msg);
                actions.add(new Redirect(new WebPageRequest("PlayMessage")));
            }
            else
            {
                msg.addText("End of messages. Goodbye.");
                actions.add(msg);
            }
            db.deleteMessageFilename(messageId);
        }

        // Respond
        token = Integer.toString(nextMessageId);
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class MessageKeep extends HttpServlet
{
    private static final long serialVersionUID = 4847733899330443860L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        // The token should be the index of the message file to keep
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        String token = ourRequest.getInstanceInfo().getToken();
        int messageId = Integer.parseInt(token);
        MessageFilenameDatabase db = new MessageFilenameDatabase();
        String filename = db.getMessageFilename(messageId);

        // Set up the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();
        int nextMessageId = -1;
        if (filename != null)
        {
            actions.add(Play.sayText("Message saved."));

            nextMessageId = db.getNextMessageId(messageId);

            if (nextMessageId != -1)
            {
                actions.add(Play.sayText("Next message."));
                actions.add(new Redirect(new WebPageRequest("PlayMessage")));
            }
            else
            {
                actions.add(Play.sayText("End of messages. Goodbye."));
            }
        }

        // Respond
        token = Integer.toString(nextMessageId);
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class RecordMessage extends HttpServlet
{
    private static final long serialVersionUID = -3436973162929428486L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        String token = ourRequest.getInstanceInfo().getToken();

        // Setup the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();

        actions.add(Play.sayText("Please leave a message after the beep. Press hash to end the recording or press star to cancel."));

        Record recordAction = new Record(new WebPageRequest("RecordComplete"));
        recordAction.setBeepOnStart(true);
        recordAction.setBargeInDigits("#*");
        recordAction.setMillisecondsMaxSilence(5000);
        actions.add(recordAction);

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class RecordComplete extends HttpServlet
{
    private static final long serialVersionUID = 3418571655744931314L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        String token = ourRequest.getInstanceInfo().getToken();
        RecordResult result = (RecordResult)ourRequest.getInstanceInfo().getActionResult();

        // Set up the actions
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();

        if ( result.getReason().equals("bargein") && result.getBargeInDigit().equals("*"))
        {
            actions.add(Play.sayText("Message recording cancelled. Goodbye."));
            actions.add(new DeleteFile(result.getFilename()));
        }
        else if (result.containsSound())
        {
            // Store the filename in our message file
            String filename = result.getFilename();

            MessageFilenameDatabase db = new MessageFilenameDatabase();
            db.appendMessageFilename(filename);

            actions.add(Play.sayText("Thanks for leaving a message. Goodbye."));
        }
        else
        {
            actions.add(Play.sayText("There was no message recorded. Goodbye."));
            actions.add(new DeleteFile(result.getFilename()));
        }

        // Respond
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
package com.aculab.telephonyrestapi.samples;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;


public class MessageFilenameDatabase
{
    public MessageFilenameDatabase() throws IOException
    {
        File temp = File.createTempFile("temp-file", ".tmp");
        String absolutePath = temp.getAbsolutePath();
        String tempFilePath = absolutePath.substring(0, absolutePath.lastIndexOf(File.separator) + 1);
        temp.delete();

        dbFile = new File(tempFilePath + "JavaVoicemailMessages.tmp");
        if (dbFile.exists())
        {
            LinkedHashMap<Integer, String> rows = getRows();

            for (Entry<Integer, String> row : rows.entrySet())
            {
                int id = row.getKey();
                if (id >= nextId)
                {
                    nextId = id + 1;
                }
            }
        }
    }

    public Integer getFirstMessageId() throws IOException
    {
        LinkedHashMap<Integer, String> rows = getRows();

        Iterator<Entry<Integer, String>> it = rows.entrySet().iterator();

        if(it.hasNext())
        {
            Map.Entry<Integer, String> row = (Map.Entry<Integer, String>)it.next();
            return row.getKey();
        }

        return -1;
    }

    public Integer getNextMessageId(Integer id) throws IOException
    {
        LinkedHashMap<Integer, String> rows = getRows();

        Iterator<Entry<Integer, String>> it = rows.entrySet().iterator();

        while(it.hasNext())
        {
            Map.Entry<Integer, String> row;

            row = (Map.Entry<Integer, String>)it.next();
            if (row.getKey() == id)
            {
                if (it.hasNext())
                {
                    row = (Map.Entry<Integer, String>)it.next();
                    return row.getKey();
                }
            }
        }

        return -1;
    }

    public String getMessageFilename(int id) throws IOException
    {
        LinkedHashMap<Integer, String> rows = getRows();

        for (Entry<Integer, String> row : rows.entrySet())
        {
            if (id == row.getKey())
            {
                return row.getValue();
            }
        }

        return null;
    }

    public void appendMessageFilename(String filename) throws IOException
    {       
        LinkedHashMap<Integer, String> rows = getRows();

        rows.put(nextId, filename);
        nextId++;
        saveDatabaseFile(rows);
    }

    public void deleteMessageFilename(int id) throws IOException
    {
        LinkedHashMap<Integer, String> rows = getRows();

        if (rows.remove(id) != null)
        {
            saveDatabaseFile(rows);
        }
    }

    private void saveDatabaseFile(LinkedHashMap<Integer, String> rows) throws IOException
    {
        BufferedWriter writer = new BufferedWriter(new FileWriter(dbFile, false));

        for (Entry<Integer, String> row : rows.entrySet())
        {
            writer.write(row.getKey() + "," + row.getValue());
            writer.newLine();
        }
        writer.close();
    }

    public LinkedHashMap<Integer, String> getRows() throws IOException
    {
        LinkedHashMap<Integer, String> rows = new LinkedHashMap<Integer, String>();

        if (dbFile.exists())
        {
            List<String> messageLines;

            messageLines = Files.readAllLines(dbFile.toPath(), Charset.defaultCharset());

            for(String line : messageLines)
            {
                String[] splits = line.split(",");
                int id = Integer.parseInt(splits[0]);
                String filename = splits[1].trim();
                rows.put(id, filename);
            }
        }
        return rows;
    }

    private int nextId = 1;
    private File dbFile;
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.aculab.telephonyrestapi.*;

public class ErrorPage extends HttpServlet
{
    private static final long serialVersionUID = -4842873371047361437L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);

        ErrorResult result = ourRequest.getInstanceInfo().getErrorResult();

        String token = String.format("Action: %s\nActionIndex: %d\nResult: %s", result.getAction(), result.getActionIndex(), result.getResult());

        // Respond
        List<TelephonyAction> actions = new ArrayList<TelephonyAction>();
        TelephonyResponse ourResponse = new TelephonyResponse(actions, token);
        ourResponse.setHttpServletResponse(response);
    }
}
// Java Servlet sample for the Aculab Telephony REST API.
//

package com.aculab.telephonyrestapi.samples;

import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;
import com.aculab.telephonyrestapi.*;

public class FinalPage extends HttpServlet
{
    private static final long serialVersionUID = 5940620014313056844L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
                    throws IOException, ServletException
    {
        handleRequest(request, response);
    }

    private void handleRequest(HttpServletRequest request,
                               HttpServletResponse response) throws IOException
    {
        // Unpack the request
        TelephonyRequest ourRequest = new TelephonyRequest(request);
    }
}

Implemented as a Flask web application:

@app.route('/Voicemail', methods=['GET','POST'])
def handle_Voicemail():

    # Here we begin to create our action list in response to the query
    my_request = TelephonyRequest(request)
    app_inst_id = my_request.get_application_instance_id()
    print("Voicemail: app_inst_id='{}'".format(app_inst_id))

    db = MessageFilenameDatabase()

    # Set a next_page for each of the options
    menu_options = []
    menu_options.append(MenuOption('1', WebPage(url='RecordMessage')))
    menu_options.append(MenuOption('2', WebPage(url='GetPasswordNumber')))

    # Create the menu action
    my_menu = RunMenu(menu_options)
    my_menu.set_prompt(Play(text_to_say= ('Press 1 to leave a message. Press 2 to listen to all messages.')))

    list_of_actions = []

    # Append the menu action to the action list.
    list_of_actions.append(my_menu)
    list_of_actions.append(Play(text_to_say="Goodbye."))

    # Get the first message id in the filename database
    db = MessageFilenameDatabase()
    msg_id = db.getFirstMessageId()

    my_response = TelephonyResponse(list_of_actions, str(msg_id))
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/GetPasswordNumber', methods=['GET','POST'])
def handle_GetPasswordNumber():

    my_request = TelephonyRequest(request)
    my_token = my_request.get_token()

    # Option 2 is to listen to all the recorded messages. 
    # First we need to check that the caller is authorised to do this.
    # We configure an IVR to ask for a PIN, we use GetNumber to do this.

    my_number = GetNumber(WebPage(url='CheckPasswordNumber'))
    my_number.set_end_digit('#')
    # As with RunMenu, we provide an initial prompt.
    my_number.set_prompt(Play(text_to_say='Please enter your password number followed by the hash key.'))

    list_of_actions = []
    list_of_actions.append(my_number)
    list_of_actions.append(Play(text_to_say="Goodbye."))

    my_response = TelephonyResponse(list_of_actions, my_token)
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/CheckPasswordNumber', methods=['GET','POST'])
def handle_CheckPasswordNumber():

    my_request = TelephonyRequest(request)
    action_result = my_request.get_action_result()
    my_token = my_request.get_token()

    id_digits = ""
    if(action_result.get('action') == 'get_number'):
        id_digits = action_result.get('result').get('entered_number')

    list_of_actions = []

    # For our purposes the PIN is hard-coded to 1234
    if id_digits == '1234':
        # Redirect to the PlayMessage page, where the first message will be played.
        list_of_actions.append(Redirect(next_page=WebPage(url='PlayMessage')))
    else:
        # The PIN is not correct, say sorry and hang up. The application will
        # automatically hang up after this action since there is no redirection
        # to a next page.
        list_of_actions.append(Play(text_to_say='Sorry, that password is not correct. Goodbye.'))

    my_response = TelephonyResponse(list_of_actions, my_token)
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/PlayMessage', methods=['GET','POST'])
def handle_PlayMessage():

    my_request = TelephonyRequest(request)
    msg_id = int(my_request.get_token())

    db = MessageFilenameDatabase()
    filename = db.getMessageFilename(msg_id)

    list_of_actions = []

    if filename != "":
        list_of_actions.append(Play(file_to_play=filename))

        # Here we set a next_page for each of the three options
        menu_options = []
        menu_options.append(MenuOption('1', WebPage(url='MessageReplay')))
        menu_options.append(MenuOption('2', WebPage(url='MessageDelete')))
        menu_options.append(MenuOption('3', WebPage(url='MessageKeep')))

        my_menu = RunMenu(menu_options)
        my_menu.set_prompt(Play(text_to_say= ('Press 1 to hear the message again. '
                                                  'Press 2 to delete it, '
                                                  'or press 3 to save it.')))

        list_of_actions.append(my_menu)

        list_of_actions.append(Play(text_to_say="Goodbye."))

    else:
        list_of_actions.append(Play(text_to_say='End of messages. Goodbye.'))

    my_response = TelephonyResponse(list_of_actions, token=str(msg_id)) 
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/MessageReplay', methods=['GET','POST'])
def handle_MessageReplay():

    my_request = TelephonyRequest(request)

    my_token = my_request.get_token()

    list_of_actions = []

    list_of_actions.append(Play(text_to_say='Playing the message again.'))
    list_of_actions.append(Redirect(next_page=WebPage(url='PlayMessage')))

    my_response = TelephonyResponse(list_of_actions, my_token)
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/MessageDelete', methods=['GET','POST'])
def handle_MessageDelete():

    my_request = TelephonyRequest(request)
    msg_id = int(my_request.get_token())

    db = MessageFilenameDatabase()
    msg_filename =  db.getMessageFilename(msg_id)
    next_msg_id = db.getNextMessageId(msg_id)
    db.deleteMessage(msg_id)

    list_of_actions = []

    list_of_actions.append(DeleteFile(msg_filename))

    play_action = Play(text_to_say='Message deleted.')

    if next_msg_id != -1:
        play_action.append_text('Next message.')
        list_of_actions.append(play_action)
        list_of_actions.append(Redirect(next_page=WebPage(url='PlayMessage')))
    else:
        play_action.append_text('End of messages. Goodbye.')
        list_of_actions.append(play_action)

    my_response = TelephonyResponse(list_of_actions, token=str(next_msg_id)) 
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/MessageKeep', methods=['GET','POST'])
def handle_MessageKeep():

    my_request = TelephonyRequest(request)
    msg_id = int(my_request.get_token())

    db = MessageFilenameDatabase()
    next_msg_id = db.getNextMessageId(msg_id)

    list_of_actions = []

    list_of_actions.append(Play(text_to_say='Message saved.'))
    if next_msg_id != -1:
        list_of_actions.append(Play(text_to_say='Next message.'))
        list_of_actions.append(Redirect(next_page=WebPage(url='PlayMessage')))
    else:
        list_of_actions.append(Play(text_to_say='End of messages. Goodbye.'))

    my_response = TelephonyResponse(list_of_actions, token=str(next_msg_id)) 
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/RecordMessage', methods=['GET','POST'])
def handle_RecordMessage():

    my_request = TelephonyRequest(request)
    my_token = my_request.get_token()

    list_of_actions = []

    # Option 1 is to leave a message. We create two actions, the first is to use TTS to
    # ask the caller to begin the message after a beep, and to press # to end the recording.
    list_of_actions.append(Play(text_to_say='Please leave a message after the beep. Press hash to end the recording or press star to cancel.'))

    # The next action it to start recording. When the recording has completed, the application
    # will be directed to the my_recorded_file_page.
    list_of_actions.append(Record(barge_in_digits="#*",             # pressing # will stop the recording
                          milliseconds_max_silence=5000,            # five seconds of silence will stop the recording
                          next_page=WebPage(url='RecordComplete'),  # when recording completes, go to this page
                          beep_on_start=True))                      # before recording begins, play a beep

    my_response = TelephonyResponse(list_of_actions, my_token)
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/RecordComplete', methods=['GET','POST'])
def handle_RecordComplete():

    my_request = TelephonyRequest(request)
    my_token = my_request.get_token()
    action_result = my_request.get_action_result()
    record_result = action_result.get('result')

    list_of_actions = []

    if (record_result.get("reason") == "bargein") and (record_result.get("barge_in_digit") == "*"):

        list_of_actions.append(Play(text_to_say='Message recording cancelled. Goodbye.'))
        list_of_actions.append(DeleteFile(record_result.get("filename")))

    elif record_result.get("contains_sound"):

        filename = record_result.get("filename")
        print ("Recorded file is {0}".format(filename))

        # Store the filename in our messages file
        db = MessageFilenameDatabase()
        db.appendMessageFilename(filename)

        list_of_actions.append(Play(text_to_say='Thanks for leaving a message. Goodbye.'))

    else:

        list_of_actions.append(Play(text_to_say='There was no message recorded. Goodbye.'))
        list_of_actions.append(DeleteFile(record_result.get("filename")))

    my_response = TelephonyResponse(list_of_actions, my_token)
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
class MessageFilenameDatabase:

    # Class to store message entries that are read from the database file.
    class Row:
        def __init__(self, id, filename):
            self.id = id
            self.filename = filename

    # Create the database file if it does not already exist
    def __init__(self):
        self.db_filename = tempfile.gettempdir() + '/' + "PythonVoicemailMessages.tmp"
        fh = open(self.db_filename, "a")
        fh.close()

    # Read the database file, parse each line into
    # a list of 'Row' classes
    def getRows(self):
        fh = open(self.db_filename, "r")
        rows = []
        for x in fh:
            id_and_filename = x.split(",")
            id = int(id_and_filename[0])
            filename = id_and_filename[1].strip()
            rows.append(self.Row(id,filename))
        fh.close()
        return rows

    # Return the id of the first (top most) message that
    # is stored in the database file.
    # Returns -1 if the first message id could not be found.
    def getFirstMessageId(self):
        rows = self.getRows()
        if len(rows) > 0:
            return rows[0].id
        return -1

    # Return the id of the message that is stored below this_id in 
    # the database file.
    # Returns -1 if the next message id could not be found.
    def getNextMessageId(self, this_id):
        rows = self.getRows()
        return_next_id = False
        for row in rows:
            if return_next_id == True:
                return row.id
            elif row.id == this_id:
                return_next_id = True
        return -1

    # Return a message filename given its id.
    def getMessageFilename(self, id):
        rows = self.getRows()
        for row in rows:
            if row.id == id:
                return row.filename
        return ""

    # Add another message entry to the database file
    def appendMessageFilename(self, filename):
        new_id = 1
        rows = self.getRows()

        for row in rows:
            if new_id <= row.id:
                new_id = row.id + 1

        fh = open(self.db_filename, "a")
        fh.write("{0},{1}\n".format(new_id, filename))
        fh.close()

    # Delete a message from the database file given its id.
    def deleteMessage(self, id):
        rows = self.getRows()
        new_rows = []

        # Clear the file contents. Re-write the entries but
        # miss out the one we want to delete.
        fh = open(self.db_filename, "w")
        for row in rows:
            if id != row.id:
                fh.write("{0},{1}\n".format(row.id, row.filename))
        fh.close()
@app.route('/ErrorPage', methods=['GET','POST'])
def handle_ErrorPage():

    my_request = TelephonyRequest(request)
    token = my_request.get_token()
    app_inst_id = my_request.get_application_instance_id()
    error_result_dict = my_request.get_error_result()
    action_string = error_result_dict.get('action', "?")
    result_string = error_result_dict.get('result', "?")
    print("ErrorPage: app_inst_id='{}' token='{}' action='{}' result='{}'".format(app_inst_id, token, action_string, result_string))

    my_response = TelephonyResponse([Play(text_to_say='An error has occurred')])
    return Response(response=my_response.get_json(), content_type='application/json;charset=utf-8')
@app.route('/FinalPage', methods=['GET','POST'])
def handle_FinalPage():
    # The FinalPage handler follows the guidelines on:
    # https://www.aculab.com/cloud/voice-and-fax-apis/rest-api/rest-api-version-2/writing-a-rest-application
    # The guidelines are:
    #   "Your final page should return an empty response, a 204 is preferable, but empty JSON is acceptable."
    my_request = TelephonyRequest(request)
    token = my_request.get_token()
    app_inst_id = my_request.get_application_instance_id()
    print("FinalPage: app_inst_id='{}' token='{}'".format(app_inst_id, token))
    empty_json = '{}'.encode('utf-8')
    return Response(response=empty_json, status=204, content_type='application/json;charset=utf-8')
<?php
require __DIR__ . "/../../autoload.php";

require __DIR__ . "/MessageFilenameDatabase.php";

use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\RunMenu;

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

$resp = new Response();
$resp->setToken( (string)getFirstMessageId() );

// Create the menu action
$menuPrompt = Play::sayText("Press 1 to leave a message. Press 2 to listen to all messages.");
$menu = new RunMenu();
$menu->setPrompt($menuPrompt);
$menu->addMenuOption('1', 'RecordMessage.php');
$menu->addMenuOption('2', 'GetPasswordNumber.php');

$resp->addAction($menu);

$resp->addAction(Play::sayText('Goodbye.'));

print $resp;
<?php
require __DIR__ . "/../../autoload.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\GetNumber;
use \Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();

$response = new Response();
$response->setToken($info->getToken()); // NB not really needed as we are not changing the token

// Create the get number action
$prompt = Play::sayText("Please enter your password number followed by the hash key.");
$getNumberAction = new GetNumber("CheckPasswordNumber.php");
$getNumberAction->setPrompt($prompt);
$getNumberAction->setEndDigit('#');

$response->addAction($getNumberAction);


$response->addAction(Play::sayText('Goodbye.'));

print $response;
<?php
require __DIR__ . "/../../autoload.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\InstanceInfo;
use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\Redirect;

$info = InstanceInfo::getInstanceInfo();

$response = new Response();
$response->setToken($info->getToken()); // NB not really needed as we are not changing the token

$enteredNumber = '';
// Get the result of the run_menu action
$result = $info->getActionResult();
if ($result != null && is_a($result, '\Aculab\TelephonyRestAPI\GetNumberResult')) {
    $enteredNumber = $result->getEnteredNumber();
}

if ($enteredNumber == '1234') {
    $response->addAction(new Redirect('PlayMessage.php'));
} else {
    $response->addAction(Play::sayText('Sorry, that password is not correct. Goodbye.'));
}

print $response;
<?php
require __DIR__ . "/../../autoload.php";

require __DIR__ . "/MessageFilenameDatabase.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\RunMenu;
use \Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();
$midstr = $info->getToken();
$mid = (int)$midstr;
$fname = getMessageFilename($mid);

$response = new Response();
$response->setToken($info->getToken()); // NB not really needed as we are not changing the token

if (is_null($fname)) {
    $response->addAction(Play::sayText('End of messages. Goodbye.'));
} else {
    $response->addAction(Play::playFile($fname));

    // Create the menu action
    $menuPrompt = Play::sayText("Press 1 to hear the message again. Press 2 to delete it, or press 3 to save it.");
    $menu = new RunMenu();
    $menu->setPrompt($menuPrompt);
    $menu->addMenuOption('1', 'MessageReplay.php');
    $menu->addMenuOption('2', 'MessageDelete.php');
    $menu->addMenuOption('3', 'MessageKeep.php');

    $response->addAction($menu);

    $response->addAction(Play::sayText('Goodbye.'));
}

print $response;
<?php
require __DIR__ . "/../../autoload.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\InstanceInfo;
use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\Redirect;

$info = InstanceInfo::getInstanceInfo();

$response = new Response();
$response->setToken($info->getToken()); // NB not really needed as we are not changing the token

$response->addAction(Play::sayText('Playing the message again.'));
$response->addAction(new Redirect('PlayMessage.php'));

print $response;
<?php
require __DIR__ . "/../../autoload.php";

require __DIR__ . "/MessageFilenameDatabase.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\DeleteFile;
use \Aculab\TelephonyRestAPI\Redirect;
use \Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();
$midstr = $info->getToken();
$mid = (int)$midstr;
$next = getNextMessageId($mid);

$response = new Response();

$response->addAction(Play::sayText('Message saved.'));

if ($next != -1) {
    $response->addAction(Play::sayText('Next message.'));
    $response->addAction(new Redirect('PlayMessage.php'));
} else {
    $response->addAction(Play::sayText('End of messages. Goodbye.'));
}

$response->setToken((string)$next);


print $response;
<?php
require __DIR__ . "/../../autoload.php";

require __DIR__ . "/MessageFilenameDatabase.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\DeleteFile;
use \Aculab\TelephonyRestAPI\Redirect;
use \Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();
$midstr = $info->getToken();
$mid = (int)$midstr;

// get file to delete
$fname = getMessageFilename($mid);
// get next before delete, otherwise it can't find the current one
$next = getNextMessageId($mid);
deleteMessageId($mid);

$response = new Response();
if (!is_null($fname)) {
    $response->addAction(new DeleteFile($fname));
}

$play = Play::sayText('Message deleted.');

if ($next != -1) {
    $play->addText('Next message.');
    $response->addAction($play);
    $response->addAction(new Redirect('PlayMessage.php'));
} else {
    $play->addText('End of messages. Goodbye.');
    $response->addAction($play);
}

$response->setToken((string) $next);

print $response;
<?php
require __DIR__ . "/../../autoload.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\Record;
use \Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();

$response = new Response();
$response->setToken($info->getToken()); // NB not really needed as we are not changing the token

$response->addAction(Play::sayText("Please leave a message after the beep. Press hash to end the recording or press star to cancel."));

$record = new Record('RecordComplete.php');
$record->setBeepOnStart(true);
$record->setBargeInDigits('#*');
$record->setMillisecondsMaxSilence(5000);
$response->addAction($record);

print $response;
<?php
require __DIR__ . "/../../autoload.php";

require __DIR__ . "/MessageFilenameDatabase.php";

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: application/json; charset=UTF-8");

use \Aculab\TelephonyRestAPI\Response;
use \Aculab\TelephonyRestAPI\DeleteFile;
use \Aculab\TelephonyRestAPI\Play;
use \Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();
$recordResult = $info->getActionResult();
$fname = $recordResult->getFilename();

$response = new Response();
$response->setToken($info->getToken()); // NB not really needed as we are not changing the token

if ($recordResult->getReason() == "bargein" and $recordResult->getBargeInDigit() == "*") {
    $response->addAction(Play::sayText('Message recording cancelled. Goodbye.'));
    $response->addAction(new DeleteFile($fname));
} else if ($recordResult->getContainsSound()) {
    appendMessageFilename($fname);
    $response->addAction(Play::sayText('Thanks for leaving a message. Goodbye.'));
} else {
    $response->addAction(Play::sayText('There was no message recorded. Goodbye.'));
    $response->addAction(new DeleteFile($fname));
}

print $response;
<?php
function loadMessageDatabase() {
    $message_list_filename = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "PHPVoicemailMessages.tmp";
    $msgs = [];
    if (file_exists($message_list_filename)) {
        $messagelist = file($message_list_filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if ($messagelist) {
            foreach($messagelist as $mline) {
                list($id, $fname) = explode(',', $mline, 2);
                $msgs[(int)$id] = $fname;
            }
        }
    }
    return $msgs;
}

function saveMessageDatabase($msgs) {
    $messagelist = [];
    foreach($msgs as $id => $fname) {
        $messagelist[] = $id . ',' . $fname;
    }
    $message_list_filename = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "PHPVoicemailMessages.tmp";
    $f = fopen($message_list_filename, 'w');
    if ($f !== false) {
        fwrite($f, implode("\n", $messagelist));
        fclose($f);
    }
}

function getFirstMessageId() {
    $msgs = loadMessageDatabase();
    if (count($msgs) > 0) {
        return array_keys($msgs)[0];
    }
    return -1;
}

function getMessageFilename($msg_id) {
    $msgs = loadMessageDatabase();
    if (isset($msgs[$msg_id])) {
        return $msgs[$msg_id];
    }
    return null;
}

function getNextMessageId($msg_id) {
    $msgs = loadMessageDatabase();
    $return_next = false;
    foreach($msgs as $id => $fname) {
        if ($return_next) {
            return $id;
        }
        if ($id == $msg_id) {
            $return_next = true;
        }
    }
    return -1;
}

function appendMessageFilename($fname) {
    $msgs = loadMessageDatabase();
    $msgs[] = $fname;
    saveMessageDatabase($msgs);
}

function deleteMessageId($msg_id) {
    $msgs = loadMessageDatabase();
    if (isset($msgs[$msg_id])) {
        unset($msgs[$msg_id]);
        saveMessageDatabase($msgs);
    }
}
<?php
header("Content-Type: application/json; charset=UTF-8");

require __DIR__ . "/../../autoload.php";

use Aculab\TelephonyRestAPI\Play;
use Aculab\TelephonyRestAPI\Response;
use Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();

$error = $info->getErrorResult();
$action = $error->getAction();
$desc = $error->getResult();
if (!is_null($action)) {
    error_log("Error from action \"$action\" with result:" . PHP_EOL . "$desc" . PHP_EOL);
} else {
    error_log("Error result:" . PHP_EOL . "$desc" . PHP_EOL);
}

$response = new Response();
$response->setToken('Error');

$play = new Play();
$play->addText('An error has occurred.');
$response->addAction($play);

print $response;
<?php
require __DIR__ . "/../../autoload.php";

http_response_code(204);
header("Content-Type: application/json; charset=UTF-8");

use Aculab\TelephonyRestAPI\InstanceInfo;

$info = InstanceInfo::getInstanceInfo();
$call = $info->getThisCallInfo();
$callid = $call->getCallId();
$duration = $call->getSecondsCallDuration();
error_log("This call id: $callid" . PHP_EOL . "This call duration: $duration" . PHP_EOL);