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?

  • It seems obvious but, if you ask a question about converting something to Apex, please include the [apex] tag. – Adrian Larson Dec 14 '18 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? – Rogerio Honorio Dec 14 '18 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! :) – PatMcClellan__c Dec 14 '18 at 16:49
3

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');
| improve this answer | |
  • @PatMcClellan__c You're welcome! Always glad to help! – sfdcfox Dec 14 '18 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.

| improve this answer | |
  • Thanks for that resource -- very helpful. And that's a good tip on not encoding the & and = parts. – PatMcClellan__c Dec 14 '18 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. – PatMcClellan__c Dec 14 '18 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.