Skip to content

Instantly share code, notes, and snippets.

@thiseldo thiseldo/ Secret
Created May 7, 2014

What would you like to do?
Simple Twilio based IVR

The Twilio node is not needed to handle voice requests as this can be accomplished using regular HTTP input and output nodes. The way it works is that when a call is received on your Twilio number, the Twilio server will pass the information to you via the Voice URL you define in your number setup. The Twilio requests require a response that is the TWiML payload. This forms the basis of the outgoing message. Full details of the TWiML markup are found in the Twilio help pages. The example flows use the HTTP input and output nodes to provide the link to Twilio.

My Node-RED flow listens for a /twiliovoice url. This must be accessible from outside of your local network. I did this using my DuckDNS account. My router then uses port forwarding to redirect the incoming request to my linux server running Node-RED . The external URL uses different ports to Node-RED and this is all catered for in the port forwarding on the router.

Your Twilio number needs to be have the correct Request URL and method setting up. This flow uses the POST method. You can also set a fallback URL by selecting the Optional settings. This ideally would be a simple TWiML file located on an externally hosted URL to respond when your Node-RED system fails to respond for any reason. At present I've not set this up.

The flows here demonstrate a simple voice menu with two choices, one to read a global context value and return the current power usage. The second is to control a device, in this case publish a value to a MQTT topic that is picked up by another subscriber to ring a bell.

The final part of the flows is to convert the created JSON body to the XML based TWiML response. This is done using the handy Json2XML node.

The flows can be further extended to handle more commands by adding more conditions to the switch node. The main thing to remember is that the flows must return a valid TWiML response in the payload.

To aid in debugging this functionality, use the Twilio dev-tools menu to show any failures.

Your Twilio account has a lot of information that includes the dev tools plus what requests and responses have been processed.

[{"id":"3fd7311e.c028ce","type":"mqtt-broker","broker":"localhost","port":"1883","clientid":""},{"id":"698c27e6.9673d8","type":"json2xml","name":"J2XML TwiML Response","root":"Response","x":717,"y":283,"z":"8a59ed8a.75a61","wires":[["f767454d.0898b8","c8cdcfd4.37323"]]},{"id":"f767454d.0898b8","type":"http response","name":"Send TwiML","x":958,"y":283,"z":"8a59ed8a.75a61","wires":[]},{"id":"6c74d317.938b2c","type":"http in","name":"TwilioVoice incoming","url":"/twiliovoice","method":"post","x":121,"y":280,"z":"8a59ed8a.75a61","wires":[["88efab86.771058","52a0fad4.ad5f04"]]},{"id":"88efab86.771058","type":"function","name":"Process Call","func":"// Create JSON object for payload response\n\nvar messageObj = { \"Gather\": [{ \"@\": { \"action\": \"http://yourhost:port/twilioivr\",\"numDigits\":\"1\"},\n\t\t \"Say\": [ { \"@\": { \"voice\": \"woman\" }, \"#\": \"Welcome to Node-Red. For power usage press 1. To ring the bell press 2. For anything else press 3\" }]}],\n\t\t \"Say\": [ { \"@\": { \"voice\": \"woman\" }, \"#\": \"You didnt press a key, goodbye\"}]};\n/* The sort of example XML we are trying to create is shown below:\n\n <Gather action=\"handle-user-input.php\" numDigits=\"1\">\n <Say>Welcome to TPS.</Say>\n <Say>For store hours, press 1.</Say>\n <Say>To speak to an agent, press 2.</Say>\n <Say>To check your package status, press 3.</Say>\n </Gather>\n <!-- If customer doesn't input anything, prompt and try again. -->\n <Say>Sorry, I didn't get your response.</Say>\n*/\nmsg.payload = messageObj;\nreturn msg;","outputs":1,"x":433,"y":282,"z":"8a59ed8a.75a61","wires":[["698c27e6.9673d8"]]},{"id":"49cc3109.b633d","type":"http in","name":"TwilioVoice Handle","url":"/twilioivr","method":"post","x":125,"y":360,"z":"8a59ed8a.75a61","wires":[["b07a8d73.4f857","21e1cdbe.de1e32"]]},{"id":"b07a8d73.4f857","type":"function","name":"Handle Voice","func":"// Use 2 outputs, 2nd is for mqtt topic/payload combination\n// Could be useful to post other caller info to a topic\nvar mqttOut = null;\nvar responseMsg = \"\";\n\nvar name = \"wholehouse\";\nvar reading =[name];\nvar digit = msg.payload.Digits;\nif( digit == \"1\" ) {\n\tresponseMsg = \"Your power usage is now \" + reading + \" Watts.\";\n} else if( digit == \"2\" ) {\n\tresponseMsg = \"Ring my bell.\";\n\tmqttOut = {topic: \"bellduino/ring\", payload: \"ringmybell\" };\n} else {\n\tresponseMsg = \"You pressed \" + digit;\n}\n\n// Create JSON object for payload response\nvar messageObj = { \"Say\": [ { \"@\": { \"voice\": \"woman\" }, \"#\": responseMsg } ] } ;\n\nmsg.payload = messageObj;\nreturn [msg, mqttOut];","outputs":"2","x":433,"y":369,"z":"8a59ed8a.75a61","wires":[["698c27e6.9673d8"],["753eb82a.8ac148","a7f09d49.580f6"]]},{"id":"c8cdcfd4.37323","type":"debug","name":"","active":true,"complete":false,"x":952,"y":390,"z":"8a59ed8a.75a61","wires":[]},{"id":"21e1cdbe.de1e32","type":"debug","name":"","active":true,"complete":false,"x":422,"y":426,"z":"8a59ed8a.75a61","wires":[]},{"id":"753eb82a.8ac148","type":"mqtt out","name":"Post message","topic":"","broker":"3fd7311e.c028ce","x":728,"y":391,"z":"8a59ed8a.75a61","wires":[]},{"id":"a7f09d49.580f6","type":"debug","name":"","active":true,"console":"false","complete":"true","x":702,"y":437,"z":"8a59ed8a.75a61","wires":[]},{"id":"52a0fad4.ad5f04","type":"debug","name":"","active":true,"console":false,"complete":false,"x":430,"y":214,"z":"8a59ed8a.75a61","wires":[]}]

This comment has been minimized.

Copy link

webmutation commented Sep 1, 2014

For the json2xml node this is what i get.

This node is a type unknown to your installation of Node-RED.
If you deploy with the node in this state, it will lose all of its configuration.
See the Info side bar for more help
Any ideas on how to deploy this?


This comment has been minimized.

Copy link

saket424 commented Feb 20, 2016

Here is a snippet without json2xml. Be sure to replace with your node-red IP and port

[{"id":"6a16e094.95e92","type":"inject","z":"fce70880.0318f8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":222,"y":291,"wires":[["99750490.668af8"]]},{"id":"6163845a.9e9c7c","type":"http response","z":"fce70880.0318f8","name":"Send TwiML","x":662,"y":165,"wires":[]},{"id":"cfd36806.302c98","type":"http in","z":"fce70880.0318f8","name":"TwilioVoice incoming","url":"/twiliovoice","method":"post","x":105,"y":248,"wires":[["99750490.668af8","fe3c68ac.01c398"]]},{"id":"99750490.668af8","type":"function","z":"fce70880.0318f8","name":"Process Call","func":"// Create JSON object for payload response\n/\nvar messageObj = { "Gather": [{ "@": { "action": "","numDigits":"1"},\n\t\t "Say": [ { "@": { "voice": "woman" }, "#": "Welcome to Node-Red. For power usage press 1. To ring the bell press 2. For anything else press 3" }]}],\n\t\t "Say": [ { "@": { "voice": "woman" }, "#": "You didnt press a key, goodbye"}]};\n/\n//var messageObj = {"Response": { "Say": "Welcome to Node-Red."}};\n\n/* The sort of example XML we are trying to create is shown below:\n\n <Gather action="handle-user-input.php" numDigits="1">\n Welcome to TPS.\n For store hours, press 1.\n To speak to an agent, press 2.\n To check your package status, press 3.\n \n \n Sorry, I didn't get your response.\n*/\n\nvar twiml_response = " \n \n <Gather action=\"\" numDigits=\"1\"> \n <Say voice=\"woman\">Welcome to Node-Red. For power usage press 1. To ring the bell press 2. For anything else press 3 \n \n <Say voice=\"woman\">Sorry, I didn't get your response. \n ";\nmsg.payload = twiml_response;\nreturn msg;","outputs":1,"noerr":0,"x":417,"y":249,"wires":[["6163845a.9e9c7c","82e22444.7d1dd8"]]},{"id":"12a09218.ed5f6e","type":"http in","z":"fce70880.0318f8","name":"TwilioVoice Handle","url":"/twilioivr","method":"post","x":109,"y":328,"wires":[["14ee455a.eb11bb","faabe843.055418"]]},{"id":"82e22444.7d1dd8","type":"debug","z":"fce70880.0318f8","name":"","active":true,"complete":false,"x":654,"y":293,"wires":[]},{"id":"14ee455a.eb11bb","type":"debug","z":"fce70880.0318f8","name":"","active":true,"complete":false,"x":406,"y":394,"wires":[]},{"id":"fe3c68ac.01c398","type":"debug","z":"fce70880.0318f8","name":"","active":false,"console":false,"complete":"payload","x":414,"y":182,"wires":[]},{"id":"faabe843.055418","type":"function","z":"fce70880.0318f8","name":"handleGather","func":"var digit = msg.payload.Digits;\n\nresponseMsg = "You pressed " + digit;\n\n\nvar twiml_response = " \n \n <Say voice=\"woman\">" + responseMsg + " \n ";\nmsg.payload = twiml_response;\nreturn msg;","outputs":1,"noerr":0,"x":421,"y":323,"wires":[["6163845a.9e9c7c","82e22444.7d1dd8"]]}]


This comment has been minimized.

Copy link

WorkingCapital commented Sep 26, 2016

Doesn't want to import.


This comment has been minimized.

Copy link

ryansaucerman commented Apr 5, 2017

Does not want to import for me either
screenshot from 2017-04-05 16-31-35

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.