Create a HTTP POST to Twilio Functions (send SMS) - twilio

I have created a Twilio function that I would like to use to send my affiliate referral link to subscribers of an application that come through my channel.
It works fine with a static to / from number, however I would like to make the "to" field a dynamic variable that can be manipulated via a HTTP/Webhook POST when a Zapier detects a new subscriber to my Mailchimp mailing list and pass their phone number as the variable.
I am also unclear what I need to do to authenticate the client (Zapier) that is making the POST as I do not want the function open to the world to use, if any insights can be shared on this it would be sincerely appreciated - I am a very inexperienced programmer trying to learn very quickly!
#philnash - thanks for your suggestion, implementing it slowly!
Many thanks in advance!
exports.handler = function(context, event, callback) {
const appCodes = ['code1', 'code2', 'code3', 'code4']
var smsBody = refCode ();
function refCode () {
return appCodes[Math.floor((Math.random() * appCodes.length))];
};
context.getTwilioClient().messages.create({
to: '+11112223333', // How do I make this dynamic from HTTP/Zapier Webhook POST???
from: '+1444555666',
body: `Get the App: ${smsBody}`
}).then(msg => {
callback(null, msg.sid);
}).catch(err => callback(err));
}

Twilio developer evangelist here.
I presume the Zapier webhook is sending the details, including the phone number, as the body of the POST request.
All the parameters in a request body appear on the event object that is passed into your handler. You probably want to run a test where you print out the contents of the event object to see what you are being passed. You can do this with:
exports.handler = function(context, event, callback) {
for (let key in event) {
console.log(`${key}: ${event[key]}`);
}
// ... rest of the function
}
Then, when you figure out what parameter is storing the number, you can use that in the call to create the message.
Let me know if that helps at all.

Try this:
exports.handler = function(context, event, callback) {
for (let key in event) {
console.log(`${key}: ${event[key]}`);
}
// ... rest of the function
callback(null, 'complete');
};

Thanks everyone for your input, it was sincerely appreciated! I was able to solve this with the following code:
exports.handler = function(context, event, callback) {
const appCodes = ['code1', 'code2', 'code3', 'code4']
var smsBody = refCode ();
var subNum = event.primaryPhone || 'There is no subscriber number'; // primaryPhone sent via HTTP post to twilio function
function refCode () {
return appCodes[Math.floor((Math.random() * appCodes.length))];
};
context.getTwilioClient().messages.create({
to: `${subNum}`, // parameters & values recieved from HTTP POST are available within the twilio functions "event" context
from: '+1444555666',
body: `Get the App: ${smsBody}`
}).then(msg => {
callback(null, msg.sid);
}).catch(err => callback(err));
}

Related

how to get dynamically the caller phone number and pass it as one of JSON key in Twilio run function wdget?

I am new in Twilio and trying to develop an IVR that at some point runs a function (Run Fuuntion Widget). The function should send http request including the user phone number to a service provider to make payment for a product selected by the user.
I tryed the code bellow but the variable is not getting the user phone number.
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
var Client = require("#paymentsds/mpesa").Client;
var phoneNumber = event.From
var phoneNumberNorm = phoneNumber.substring(4);
var client = new Client({
apiKey: 'xxxxxxxxxxxxxxxxxx', // API Key
publicKey: 'xxxxxxxxxxxxxxxxxx', // Public Key
serviceProviderCode: '171717' // input_ServiceProviderCode
});
var paymentData = {};
paymentData['from'] = phoneNumberNorm;
paymentData['reference'] = '11114';
paymentData['transaction'] = 'T12344CC';
paymentData['amount'] = '10';
client.receive(paymentData).then(function(r) {
// Handle success scenario
twiml.say("Your payment was successfull");
callback(null, twiml);
}).catch(function(e) {
// Handle success scenario
twiml.say("Your payment failed");
callback(null, twiml);
});
};
Twilio developer evangelist here.
You will need to pass the phone number into the Function via the Run Function widget.
You need to add a Function Parameter, for example From, and then the data you want to pass, which for the incoming phone number is {{contact.channel.address}}. Like so:
Then you can use event.From in your Function code.

Twilio Functions Error 20429 - Too many requests multiple sms messages

I am using Twilio functions and Programable SMS to send SMS Messages to a list of numbers form my iOS App. There are just over 100 mobile numbers (113 on the time of this error) in the list. Most of these messages send but then the function says that it timed out after 502ms.
I am using the example code from Twilio to send to group messages (that I have copied below) and making a URLSession request from my iOS app.
Is there a way that I can fix this issue so that I can send to this fairly large list of phone numbers or make the function run for longer?
Thank you very much for your help.
Tom
Request:
let messagesPhoneNumberString = [+447987654321abc,+447123789456def,+447123456789ghi]
"https://myfunction.twil.io/send-messages?phoneAndID=\(messagesPhoneNumberString)&globalID=\(current globalID)"
My Twilio Function Code:
exports.handler = function(context, event, callback) {
let phoneAndIDString = event['phoneAndID'];
let globalID String = event['globalID'];
let numbersArray = phoneAndIDString.split(",");
Promise.all(
numbersArray(number => {
let phoneNumberSplit = "+" + number.slice(1, 13);
let idSplit = number.slice(13);
console.log('Send to number: ' + phoneNumberSplit + ' - with ID: ' + idSplit);
return context.getTwilioClient().messages.create({
to: phoneNumberSplit,
from: 'Example',
body: 'Hello World: ' + idSplit
});
})
)
.then(messages => {
console.log('Messages sent!');
callback(null, response);
})
.catch(err => console.error(err));
};
Twilio developer evangelist here.
Twilio Functions has a timeout of 5 seconds, so it is likely not the best idea to use a Twilio Function to send that many messages in one go.
You have some options though.
If you are sending all those numbers the same message then you could use the Twilio Notify passthrough API. Check out the details in this blog post about sending mass messages with Node.js.
Otherwise, if you have to send different messages then you could split up the numbers into batches and use the same function multiple times.
Finally, you could use a different platform to send the messages that doesn't have a 5 second limit.
In addition to the options provided in Phil's answer you could use recursion.
You could trigger the process from your app and pass all numbers in the initial function call just like you do now.
Then, the idea is to send just one message per function call and let the Twilio function call itself after it receives the response from .create(). This means no concurent calls to send messages, messages are sent one after another though the order in which they are received is not necessary the order in which the numbers are passed in the query string.
You'll need to add axios in the function dependencies configuration (https://www.twilio.com/console/runtime/functions/configure).
Axios is used to make the HTTP request to the function from within the function.
Each function run, tests for the stop condition which happens when the phone numbers query string length is zero. Then, uses .shift() to remove the first element from the numbers array to work with it. The remaining array is passed to the next function call.
This is the code I've tried, and it worked for me, but you'll have to change (the 11 length on .slice() method) for +44 because I've tested with US numbers +1 which are shorter in length.
exports.handler = function(context, event, callback) {
const axios = require("axios");
let phoneAndIDString = event["phoneAndID"].trim();
console.log(phoneAndIDString);
let globalIDString = event["globalID"].trim();
// stop condition for recursive call
if (phoneAndIDString.length === 0) {
return callback(null, true);
}
let numbersArray = phoneAndIDString.split(",");
console.log(numbersArray);
// take the first item of array
let number = numbersArray.shift();
console.log(number);
// the remaining array will be passed to the next function call
phoneAndIDString = numbersArray.join();
console.log(phoneAndIDString);
let phoneNumberSplit = "+" + number.slice(0, 11);
let idSplit = number.slice(11);
console.log("Send to number: " + phoneNumberSplit + " - with ID: " + idSplit);
context
.getTwilioClient()
.messages.create({
to: phoneNumberSplit,
from: "+17775553333",
body: "Hello World: " + idSplit
})
.then(msg => {
console.log("Message sent: " + msg.sid);
axios
.get(
"https://foo-bar-1234.twil.io/send-messages?globalID=baz&phoneAndID=" +
phoneAndIDString
)
.then(function(response) {
// handle success
console.log(response.status);
return callback(null, true);
})
.catch(function(err) {
// handle error
console.error(err);
});
})
.catch(function(err) {
console.error(err);
});
};
Try to go step by step with it while testing, console log things and then return early with return callback(null, true) as you go from top to bottom so you make sure you don't go in a loop.

How to properly Loop through SMS sending via Twilio Functions

I have Twilio Studio calling a Twilio Function and need it to send email to a variable list (small list) of emails. This question is mostly around looping through them as I can pass variables just fine. I have an array of emails to send a text to and am in a Twilio Function. But all examples I find online are about sending to just ONE. Part of me thinks this needs to be a Twilio Function calling another Twilio function (one loops, the other sends Emails)... but I can't figure out a way to do that. If I could contain it to one Twilio function, that would be great.
I have Twilio Studio calling a Twilio function. I need to keep this all on Twilio... so looping through via PHP and running functions one at a time through there doesn't work. I need this to run on Twilio's serverless setup.
Here's an example of what I have that works:
exports.handler = function(context, event, callback) {
// using SendGrid's v3 Node.js Library
// https://github.com/sendgrid/sendgrid-nodejs
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(context.SENDGRID_API_KEY);
const msg = {
to: 'me#example.com',
from: 'noreply#example.com',
templateId: 'my-id-goes-here',
dynamic_template_data: {
recipient_name: 'John Smith'
}
};
sgMail.send(msg).then(response => {
let twiml = new Twilio.twiml.MessagingResponse();
callback(null, twiml);
})
.catch(err => {
callback(err);
});
};
Here's me trying to loop through in similar fashion and failing
exports.handler = function(context, event, callback) {
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(context.SENDGRID_API_KEY);
var responder_emails = 'me#example.com,me+test1#example.com';
var emails_a = responder_emails.split(',');
emails_a.forEach(function(responder_email) {
const msg = {
to: responder_email,
from: 'noreply#example.com',
templateId: 'my-id-goes-here',
dynamic_template_data: {
recipient_name: 'John Smith'
}
};
sgMail.send(msg);
});
callback();
};
I can pass in multiple emails into a Twilio function... I'm just not sure how to loop through correctly.
Heyo. Twilio Evangelist here. 👋
In your first example, you rightfully waited for the send call to be done by using then. In your second example, you missed that. You run several send calls but immediately call callback without waiting.
A fixed (roughly prototyped version) could look as follows.
exports.handler = function(context, event, callback) {
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(context.SENDGRID_API_KEY);
var responder_emails = 'me#example.com,me+test1#example.com';
var emails_a = responder_emails.split(',');
Promise.all(emails_a.map(function(responder_email) {
const msg = {
to: responder_email,
from: 'noreply#example.com',
templateId: 'my-id-goes-here',
dynamic_template_data: {
recipient_name: 'John Smith'
}
};
return sgMail.send(msg);
})).then(function() {
callback();
}).catch(function(e) {
callback(e);
})
});
You have already an array of emails because you called split. You can use this array in combination with Array.map and Promise.all.
Map basically iterates over your array and lets you create a new array with whatever you return from the function inside of map. What the code above does is that it transforms [email, email] to [Promise, Promise]. The promises are the return value of sgMail.send.
Now, that you have an array holding promises that will resolve when sendgrid accepted your call, you can use Promise.all. This method waits for all the promises to be resolved (or rejected) and returns itself a new promise which you can use then with. When all sendgrid calls are done it's time to finish the function by calling the function callback.
Side note: this "map/Promise.all" trick performs all send grid calls in parallel. There might be situations where you want to call them one after another (saying you are doing a lot of calls and run into rate limiting).
Hope that helps and let me know how it goes. :)

Call forwarding + SMS + Whisper?

I'm trying to implement a Twilio function to (1) forward calls to my personal phone, (2) send a "heads up" SMS just before, and (3) say a whisper prior to connecting. I've been able to set up Twilio to do any 2 of the previous 3 things but never the 3 at the same time!
exports.handler = function(context, event, callback) {
// Get an initialized Twilio API client
const client = context.getTwilioClient();
twilioClient.messages.create({
to: 'PERSONAL_PHONE',
from: 'TWILIO_PHONE',
body: 'Incoming!!!'
}).then(function() {
const twiml = new Twilio.twiml.VoiceResponse();
twiml.dial.number({ url: WHISPER_URL }, 'PERSONAL_PHONE');
callback(null, twiml);
});
};
When implementing this, it sends the SMS but the call never connects (and the calling party hears an error message).
Would really appreciate a lesson here :)
Thank you!
Btw, I found a solution:
exports.handler = function(context, event, callback) {
// Get an initialized Twilio API client
const client = context.getTwilioClient();
twilioClient.messages.create({
to: 'NUMBER',
from: 'TWILIO_PHONE',
body: 'Incoming!!!'
}).then(function() {
const twiml = new Twilio.twiml.VoiceResponse();
const dialobj = twiml.dial();
dialobj.number({url:'WHISPER_URL'},'NUMBER');
callback(null, twiml);
});
};

Twilio statusCallback doesn't fire

I'm trying to set up messages delivery status check with twilio. For some reasons twilio statusCallback doesn't fire. Could you please help me to find an error?
Here is a file where I do initialization and send messages:
const Twilio = require('twilio');
const {
TWILIO_ACCOUNT_SID,
TWILIO_AUTH_TOKEN,
TWILIO_PHONE_NUMBER
} = require('config');
const client = new Twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
module.exports = {
send: (body, phoneNumber) => client.messages.create({
body,
to: phoneNumber,
from: TWILIO_PHONE_NUMBER,
statusCallback: 'http://postb.in/b/XXXXXXXX'
})
.then((message) => {
console.log(message.sid);
return message.sid;
})
};
Here is a test where I call message sending after initialization:
const { expect } = require('chai');
const smsUtility = require('utils/sms');
describe('Sms utility', () => {
it('should send a message and return message sid', (done) => {
const body = 'Body';
const number = '+XXXXXXXXXXX';
smsUtility.send(body, number)
.then((messageSid) => {
expect(typeof messageSid).to.equal('string');
return done();
})
.catch((err) => {
console.log(err);
return done();
});
});
});
I use test credentials, but when I replace it with real twilio credentials, I successfully receive a message, so this part works fine. Also, when I try to ping postb.in url manually (with curl), it also works OK. Only statusCallback doesn't work.
Thanks.
Twilio developer evangelist here.
It looks to me like you have everything set up nicely aside from your Postbin URL.
I noticed you show your URL as http://postb.in/b/XXXXXXXX. But the /b/ version of the URL is the dashboard for your Postbin. Requests to the dashboard won't show up on the dashboard.
Instead, you should use the URL that looks like: http://postb.in/XXXXXXXX. Try that and let me know if it's working.

Resources