3

I've already read and re-read the other posts here and here about cUrl to Apex. Those both dealt with only one parameter in the body of the request. This cUrl seems a bit trickier -- I can't seem to hack it.

I'm trying to make a call to Twilio's Notify API (allows you to send bulk messages in a single api call.) It works from cUrl, but I can't figure out how to parse it in Apex.

Here's the cUrl:

   curl -X POST https://notify.twilio.com/v1/Services/[messageServiceId]/Notifications \
--data-urlencode "Body=Test Message from Pat." \
--data-urlencode 'ToBinding={"address": "+18005551212", "binding_type": "sms"}' \
--data-urlencode 'ToBinding={"address": "+18005551233", "binding_type": "sms"}' \
-u [my AccountId]:[my AuthToken]

And here's what I've been trying in Apex:

Http http = new Http();
HttpRequest request = new HttpRequest();
String AccountSid = [my AccountSid];
String AuthToken = [my AuthToken];
request.setMethod('POST');
Blob headerValue = Blob.valueOf(accountSID+':'+authToken);
String authorizationHeader = 'BASIC '+ EncodingUtil.base64Encode(headerValue);
request.setHeader('Authorization', authorizationHeader);
String body = 'Test message from Pat';
List<String> phoneNumbers = new List<String>();
String baseUrl = 'https://notify.twilio.com/v1/Services/';
String ServiceSid = [messageServiceId];
String NotificationsSuffix = '/Notifications';
request.setEndpoint(baseUrl + ServiceSid + NotificationsSuffix);
String payload = '';
payload += 'Body=' + body;
payload += '&ToBinding=[{"address":"+18005551212","binding_type":"sms"},{"address":"+18005551233","binding_type":"sms"}]');
request.setBody(payload);
HttpResponse response= http.send(request);
System.debug(response);

Note that "body" is one of the required parameters for the Twilio API -- it refers to the body of the message to be sent. Therefore, I've used the variable called payload for the body of the http request.

I'm not clear if I should be using urlEncode on the body elements, or how to concatenate them. Should I be using the question mark and/or ampersand?

3
  • It seems obvious but, if you ask a question about converting something to Apex, please include the [apex] tag.
    – Adrian Larson
    Dec 14, 2018 at 3:12
  • I didn't try your code, but I didn't see you say anything about Remote Site Settings, did you try to add this site in the Remote Site settings? In order for you to do a call outside of salesforce you need to add the site in the Remote Site Settings. Also, do you get any exception? Dec 14, 2018 at 3:18
  • Rogerio, sorry I forgot to mention that -- and yes, the first time I tried running it, the error reminded me to add it as a remote site. That's the easy part! :) Dec 14, 2018 at 16:49

2 Answers 2

4

It'd probably be best if you started as a String array:

// ...
String[] payloadParts = new String[0];
String[] toBindings = new String[] { '{"address": "+18005551212", "binding_type": "sms"}', '{"address":"+18005551233","binding_type":"sms"}' };
payloadParts.add('Body='+EncodingUtil.urlEncode(body,'utf-8'));
for(String toBinding: toBindings) {
  payloadParts.add('ToBinding='+EncodingUtil.urlEncode(toBinding, 'utf-8'));
}
String finalPayload = String.join(payloadParts,'&');
request.setBody(finalPayload);
// ...

This gives you the flexibility to load the parts that you need to.

Note that basically, your main mistake is that you tried to make a JSON array, but they're really just repeated parameters, as demonstrated here.

Finally, what we're doing here is creating a "POST" and specifying the parameters as the body. This allows for "unlimited" query string lengths, and is a de facto standard using the Content-Type of application/x-www-form-urlencoded that many/all servers understand and browsers will use by default for forms.

You might want to read up on how that works in practice. Also, you may be missing the Content-Type header, so you should probably set that, too (curl does this for you automatically):

request.setHeader('Content-Type','application/x-www-form-urlencoded');
1
  • @PatMcClellan__c You're welcome! Always glad to help!
    – sfdcfox
    Dec 14, 2018 at 15:53
2

A request bin service can be helpful in showing exactly what curl generates for the outbound body content. The curl manpage is also helpful:

--data-urlencode

(HTTP) This posts data, similar to the other -d, --data options with the exception that this performs URL-encoding.

In this case, using your command line, the generated request body is

Body=Test%20Message%20from%20Pat.&ToBinding=%7B%22address%22%3A%20%22%2B18005551212%22%2C%20%22binding_type%22%3A%20%22sms%22%7D&ToBinding=%7B%22address%22%3A%20%22%2B18005551233%22%2C%20%22binding_type%22%3A%20%22sms%22%7D

The payload's specified differently in your Apex than it is in your curl command line. In Apex, you have a JSON list; in curl, you specify the parameter twice with single JSON objects. Being unfamiliar with Twilio's API, I don't know which one is correct.

Regardless, you should be URL-encoding the parameters, but not the & and = portions of the format, and using & to concatenate name-value pairs.

2
  • Thanks for that resource -- very helpful. And that's a good tip on not encoding the & and = parts. Dec 14, 2018 at 15:43
  • The docs are sketchy with regard to sending multiple ToBindings. The code sfdcfox provided works, but the docs (for Java) also show submitting a list to the ToBindings parameter. I've asked for clarification from Twilio support. In a situation where I have 10K numbers to send, putting them in a list would make it a bit shorter. Dec 14, 2018 at 16:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .