Reputation: 474
I have a webhook setup on my Twilio number to execute a C# Controller/Action when a new call comes into my number. I'm returning TwiML to Twilio without any problems. Now I need to be notified when a call moves from a ringing status to an in-progress status. I'm attempting to use a webhook in the "CALL STATUS CHANGES" field in Twilio but this seems to only fire when the call is completed:
How can I configure a webhook on an incoming call to be notified when the call moves from ringing to in-progress?
Edit Added Image showing the request sent to my webserver clearly passes a Call Status parameter with value of "ringing". Why wouldn't I receive a notification when this changes to in-progress?
Upvotes: 4
Views: 3119
Reputation: 123
Based on my research, it does not seem possible to get a callback when the call status (for an incoming call) changes to 'in-progress'. However, I think the following more or less solves recording an incoming call from the very beginning.
On an incoming call, my webhook gets hit. I use the REST API to initialize a call recording (i.e. const recordingPromise = Twilio.calls(callSID).recordings.create({// options here});
(Note recordingPromise
should have a then or catch or be dealt with when Twilio returns.)
My webhook at the end of the function returns some twiml (e.g. Say 'Hello', gather loop, routing, ultimately connect to some device). res.type('text/xml').send(twiml.toString());
I observed that most of the time this would work, but I couldn't understand why if I added an await
to the creation of the recordingPromise it would always fail. (At first I thought I was not allowed to Record and to also use Gather (since my twiml ultimately leads to a gather), but with the REST API this is actually OK. On the other hand if one uses the Record "verb", instead of the REST API, then one cannot place any other Twilio "verbs", like Gather, after it.)
I also observed that if between recordingcreation and the return of the webhook I waited a long time, that it would also fail, e.g.,
const recordingPromise = Twilio.calls(callSID).recordings.create({// options here});
// wait for a long time, e.g., 8 seconds
await new Promise(r => setTimeout(r, 8000));
// set up twiml here then return
res.type('text/xml').send(twiml.toString());
The error message I would get from twilio was that the "resource is not eligible for recording" and a pointer to error 21220 which is about attempting a realtime operation on a call that is not in-progress.
My understanding is now this: Until the webhook returns some twiml, the call is still ringing (not in-progress). Only upon returning twiml (and twilio acting on it, which is quite fast, usually) does the call stop ringing and go to in-progress. The race condition I was creating (between the real time record operation and answering the call by returning twiml) was generally working out OK. It would obviously not work if I didn't answer the call (i.e. return the twiml XML) until after the recording started (i.e. when I was awaiting the result of the start recording op before continuing... duh!)
This also meant that I shouldn't do something like
const recordingPromise = Twilio.calls(callSID).recordings.create({// options here});
await longDataBaseRequestThatAffectsTheTwimlRouting(dbReq); // this will take too long!
res.type('text/xml').send(twiml.toString());
In the end, I opted for the following
// do any time intensive ops
const recordingPromise = Twilio.calls(callSID).recordings.create({// options here});
res.type('text/xml').send(twiml.toString());
i.e., starting the recording just before returning the twiml. Swapping the order should also work, but in either case we still have a race condition on the twilio side, where we fail if the recording tries to start before the call is answered.
To make things slightly more robust, I wrapped Twilio.calls().recordings.create()
in a function that will retry.
const beginCallRecording = async (callSID, tries=0) => {
try {
const recordingResult = await twilioAuthorizedClient.calls(callSID).recordings.create({//options});
console.log(recordingPromise); // log result of call
} catch (e) {
console.log(`begincallrecording failed on ${tries}th try`);
console.log(e); // should be a "call not yet in progress error"
if (tries < 2) {
// noinspection ES6MissingAwait
beginCallRecording(callSID, tries+1);
}
}
This could still fail, and if my logs show that it is then I'll look for another workaround, set a timeout, etc.
IMO, twilio should support something akin to a "scheduleCallRecording" so that, on their side, if you try to begin recording a call when the call is not yet in progress, then it should wait and start recording once it is in progress. If the call is never answered then the recording should just be dropped. Doesn't seem like it would be that hard to support. Also, I think their documentation (e.g. on the potential race condition) could have been better.
Upvotes: 2
Reputation: 751
If you want to track the status of your calls, you can use the StatusCallbackEvent parameter to get notifications back to your application every time a call moves to a different status. For more information, please see article Tracking the Status of an Outbound Call. Documentation
Upvotes: 1
Reputation: 10781
When the call comes into Twilio, it is automatically answered. There is no ringing, it goes right to in-progress.
Are you looking for the results of another call you place using your TwiML? Then you can use the verb's noun.
TwiML Voice: https://www.twilio.com/docs/voice/twiml/number
Upvotes: 0