---title: IVR with Voicemails Forwarded to Email - Node.jsslug: /guides/nodejs-ivr-with-voicemail-to-emailsidebar_position: 3x-custom: ported_from_readme: true---## OverviewThis advanced example builds an application that implements a simple phone tree IVR with a few interesting features, including:- Parallel dialing to multiple phone numbers- Recording a voicemail and using a dynamic greeting- Transcribing voicemail and sending both the text and the recording via email## What do you need to run this code?Check out the full code on our Github Repository [here](https://github.com/signalwire/signalwire-guides/tree/master/code/node_ivr)You will need the [SignalWire Node.JS SDK](https://docs.signalwire.com/topics/laml-api/#laml-rest-api-client-libraries-and-sdks-nodejs), and your SignalWire Credentials. You can find these by logging into your SignalWire space and navigating to the API tab. For more information on navigating your SignalWire space check [here](/guides/navigating-your-space)The application also uses the [Express](https://expressjs.com/en/starter/installing.html) web framework and [Mailgun](https://www.mailgun.com/) to send the emails, and you will need an API key from that service. You could also use any other email API.## Running the application### Setup Your Environment File1. Copy from example.env2. Save new file called .env3. Fill in the fields with your own valuesPRIMARY_SALES=+15557788999SECONDARY_SALES=+15554433222RECRUITERS_GROUP=+15556677888,+15559998877ACCOUNTING_GROUP=+15554455777JOBS_EMAIL=jobs@yourdomain.comACCOUNT_EMAIL=accounts@yourdomain.comEMAIL_FROM=me@samples.mailgun.orgMAILGUN_DOMAIN=your-Mailgun-domainMAILGUN_API_KEY=your-Mailgun-api-key### Build and run via DockerIt is simpler to run the application via Docker, by first building the image with docker build -t nodeivr . followed by docker run -it --rm -p 3000:3000 --name nodeivr --env-file .env nodeivr.### Build and Run locallyIf you are running the application locally, first load the .env file with set -o allexport; source .env; set +o allexport, then run npm install followed by npm start.:::info You may need to use a SSH tunnel for testing this code if running on your local machine. – we recommend [ngrok](https://ngrok.com/). You can learn more about how to use ngrok [here](/guides/how-to-test-webhooks-with-ngrok).When you configure a SignalWire number to test, you should add /entry to the end of the URL you input, such as https://yourname.ngrok.io/entry.Otherwise, simply head to http://localhost:3000:::## Step by Step Code WalkthroughIn the [Github Repo](https://github.com/signalwire/signalwire-guides/tree/master/code/node_ivr) there are 6 files. We are mainly concerned with the env.example which we will use to set up our .env file, and index.js where the application lives.### EntryThe core of the application is the entry route, where we ask for the users choice via DTMF.jsapp.post(/entry, (req, res, next) => { var response = new RestClient.LaML.VoiceResponse(); gather = response.gather({ timeout: 5, numDigits: 1, action: formatUrl(mainmenu) }); gather.say(Hello! Press 1 for sales, 2 for recruiting or 4 for accounting.); respondAndLog(res, response);});The SignalWire Compatibility APIs require that code is translated into XML, so here is the XML version of what the code above does!xml Hello! Press 1 for sales, 2 for recruiting or 4 for accounting. ### MainMenuBased on what number the user inputs, we then redirect to the correct action in the mainmenu handler. Whether the user dials sales, recruiting, or accounting, you can see below that we dial every member of the group at the same time. When one person picks up, the rest of the calls will be terminated.jsapp.post(/mainmenu, (req, res, next) => { console.log(req.body); var response = new RestClient.LaML.VoiceResponse(); switch (req.body.Digits) { case 2: dial = response.dial({ timeout: 20, action: formatUrl(voicemail, ?Email= + JOBS_EMAIL + &Message=Recruiting), }); var recruiters = RECRUITERS_GROUP.split(,); recruiters.forEach(function (item) { dial.number(item); }); break; case 4: dial = response.dial({ timeout: 20, action: formatUrl(voicemail, ?Email= + ACCOUNT_EMAIL + &Message=Accounting), }); dial.number(ACCOUNTING_GROUP); break; default: dial = response.dial({ timeout: 20, action: formatUrl(primarysalesdial) }); dial.number(PRIMARY_SALES); } respondAndLog(res, response);});For example, if the caller enters 2, this is the XML that is returned. Multiple entries are dialed at the same time, the first one to pick up hangs up the other.xml +15556677888 +15559998877 ### Action parametersWhat is very important here is the action parameter in each dial. The voicemail URL is what will be triggered when a call ends, and we will need to check if the call had actually connected to the intended department or not. If not, we will send them to voicemail. Note that we also pass the department name via query string so the greeting is dynamic.jsapp.post(/voicemail, (req, res, next) => { var response = new RestClient.LaML.VoiceResponse(); if (req.body.DialCallStatus != completed) { // it means the call was not answered response.say( Our + req.query.Message + department is currently unavailable. Please leave a message after the beep. ); action = formatUrl(voicemailhandler, ?Email= + req.query.Email); response.record({ transcribe: true, transcribeCallback: action, action: action }); } respondAndLog(res, response);});generating the following XML:xml Our Accounting department is currently unavailable. Please leave a message after the beep. In the above, we set a transcribeCallback for the action. This allows our callback to receive both the recording file URL and the transcription text.Handling the transcription is made a bit tricky by the fact that it is asynchronous, so the handler gets called twice, once for the recording URL and once for the transcription. We get around that by temporarily saving the recording in a hash in memory. In a production environment, this should be replaced by a database.### Sending our transcription to email with MailgunWhen the second request comes with the transcription, we use Mailgun to send an email containing the recording file and the recording transcription.jsapp.post(/voicemailhandler, (req, res, next) => { if (req.body.TranscriptionText) { console.log( Got transcription, send email + req.query.Email + RECORDING_DB[req.body.CallSid] + req.body.TranscriptionText ); emailTranscription( req.query.Email, RECORDING_DB[req.body.CallSid], req.body.TranscriptionText, req.body.From ); // avoid leaking memomry delete RECORDING_DB[req.body.CallSid]; } else if (req.body.RecordingUrl) { console.log(stash recording for later); RECORDING_DB[req.body.CallSid] = req.body.RecordingUrl; } var response = new RestClient.LaML.VoiceResponse(); respondAndLog(res, response);});## Wrap UpThis guide takes look into how the SignalWire API can be used to handle parallel dialing as well as recording and transcribing voicemail. Then sending those voice mail transcriptions and recordings with the help of MailGun!### Resources[Github](https://github.com/signalwire/signalwire-guides/tree/master/code/node_ivr) [SignalWire Node.JS SDK](https://docs.signalwire.com/topics/laml-api/#laml-rest-api-client-libraries-and-sdks-nodejs) [Express](https://expressjs.com/en/starter/installing.html) [Mailgun](https://www.mailgun.com/)## Sign Up HereIf you would like to test this example out, you can create a SignalWire account and space [here](https://m.signalwire.com/signups/new?s=1).Please feel free to reach out to us on our [Community Slack](http://signalwire.community/) or create a Support ticket if you need guidance!