Reputation: 9546
My goal is to have Twilio make a call in response to an SMS, and speak the contents of the SMS. Here's my code:
const express = require('express');
const app=express();
const VoiceResponse=require('twilio').twiml.VoiceResponse;
//const MessagingResponse=require('twilio').twiml.MessagingResponse;
const bodyParser = require('body-parser');
const client=require('twilio')(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
var https=require("https");
app.use(bodyParser.urlencoded({extended: false}));
const port=process.env.PORT;
app.listen(port,()=>{
console.log('live on port '+port);
});
app.get('/',function(req,res){
res.send('this is the homepage GET response');
var url=textforspeechURL("abcde");
https.get(url,res=>{
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
});
//res.end();
});
function textforspeechURL(textforspeech){
return "https://myapp.herokuapp.com/getVoiceTwiml?textforspeech="+encodeURIComponent(textforspeech);
}
app.post('/sms',(req,res)=>{
var body=req.body.Body;
var fromObj=req.body.From;
var toObj=req.body.To;
url=textforspeechURL(body);
console.log("url to send: "+url);
client.calls.create({
url:url,
to: toObj,
from: fromObj,
method: 'GET'
});
});
app.get('/getVoiceTwiml',(req,res)=>{
const response=new VoiceResponse();
response.say(req.query.textforspeech);
responseTwiml=response.toString();
console.log("responseTwiml: "+responseTwiml);
res.send(responseTwiml);
});
When I go to the app's homepage, the console displays:
app[web.1]: BODY: <?xml version="1.0" encoding="UTF-8"?><Response><Say>abcde</Say></Response>
so /getVoiceTwiml
is executing as expected. But when I send an SMS to the app's Twilio number, the console displays:
app[web.1]: url to send: https://myapp.herokuapp.com/getVoiceTwiml?textforspeech=some%20text2018-01-07T21:21:09.875589+00:00
heroku[router]: at=error code=H12 desc="Request timeout" method=POST path="/sms/" host=myapp.herokuapp.com request_id=ac8176bb-3a1f-4752-9685-1cd8e017fdce fwd="54.208.34.131" dyno=web.1 connect=1ms service=30000ms status=503 bytes=0 protocol=https
So in the /sms
endpoint, it's successfully constructing the URL, but the client.calls.create()
call is timing out. What's wrong with how I'm creating the call?
Upvotes: 0
Views: 334
Reputation: 73057
Twilio developer evangelist here.
I'm glad you discovered your problem with the To
and From
numbers. Perhaps I can give some further guidance so that this sort of problem won't be discovered through H12 request timeouts though.
The issue is that your /sms
endpoint never made a response to the incoming request, so Node just hung there waiting, eventually timing out.
Since you're not responding to the SMS with another message, you can just respond with an empty <Response>
TwiML. You probably also want to log or throw an error if something goes wrong. You can update your /sms
endpoint to something like this instead:
app.post('/sms',(req,res)=>{
var body=req.body.Body;
var fromObj=req.body.From;
var toObj=req.body.To;
url=textforspeechURL(body);
console.log("url to send: "+url);
client.calls.create({
url:url,
to: toObj,
from: fromObj,
method: 'GET'
}, (err, apiResponse) => {
if (err) { console.error("There was an error making the call: ", err); }
res.set('Content-Type', 'application/xml');
res.send('<Response/>')
});
});
This will always respond with an empty <Response>
and let Twilio know that you got the message successfully. If there is an error in making the call, that will be logged too.
Upvotes: 1
Reputation: 9546
The problem was that I was using a From
number that was the number from the inbound SMS, and my Twilio number as the To
number. Reversing them resolved the problem.
Upvotes: 0
Reputation: 10359
From Heroku's Error code documentation:
H12 - Request timeout
An HTTP request took longer than 30 seconds to complete.
Your incoming request is taking longer than the allotted 30 seconds because of the outbound call to Twilio, which will take an indeterminate amount of time.
One way to rectify this would be to create a worker dyno and use something like node-resque to create/schedule the task.
So, the request comes in, you create/schedule a background job, return any relevant information to the caller (e.g. a job id which would allow them to check on the status of their "request") and the job is then processed "in the background" without any time constraints.
Upvotes: 0