This article pulls from an earlier article I made about Script.Util functions in SSJS (Rest API Method in SSJS).

An issue many people face when trying to have their systems talk to each other is that if there is not a pre-existing app in SFMC for their service, how do we make them talk to each other?

Many people just use bulk upload/downloads via CSV and SFTP and call it a day. This works great for high volume interactions or interactions that do not require real-time integration, but if you need to have quick, low volume integrations, this is not a viable solution.

Utilizing REST API is the go-to for these integrations in most services, but how to do this inside of SFMC is not well documented anywhere! Plus, the default functions in AMPscript and SSJS are limited and only include GET and POST methods, severely limiting your capabilities. This is where the Script.Util functions come in. For more details on using this function, please see my previous article.

To be fair, you can still use the built-in POST and GET functions successfully, but I find much better success using Script.Util because it offers many more customization options that you do not get in those functions.

For example, handling any response other than a 200 status. If the built in functions get any status code that is not an ‘OK’ response, they will toss an error and break the whole script – where as with Script.Util you can set the call to instead just return the status and you can handle it inside your own script instead of just blowing everything up.

So, we now know there is the capability in SFMC that lets us use all the methods so we can speak to our service, now what?

Environment

So, where the heck do I put this code? As this kind of interaction would require a more robust type scripting language, we would be limited to anywhere that can use AMPscript or SSJS – removing Queries and other internal UI tools from the picture.

So, now we know where not to go, so where do we actually go? Well, that depends on your need. First things first though, you cannot, nor should you attempt to, put this inside of a message context. This means no go for emails, SMS, push messages, etc.

Next, if you are looking to have user input or interaction, you should place this inside of a Cloudpage, but the Cloudpage/Browser has a built-in timeout period that is only a couple of minutes at most – meaning the integration has to be lightning quick and light-weight.

Finally, for the heavier integrations, you will need to utilize a script activity inside of Automation Studio, which has a time-out period of 30 minutes. Do note though, by default this has a minimum scheduling of hourly via Automation Studio. There are some ways to work around that, but you can then run into heavy processing draws through your enterprise which can slow down everything else you are doing – so be careful and considerate when building the process.

So…now we know where to build…now WHAT do we build there?

Functions

For the sake of giving a ‘real life’ example, I am utilizing reqres.in as a sample external service to connect to from SFMC. This is a free testing environment for REST API calls and should help provide the necessary context for sample calls that you can replicate and test out yourself.

That being said, each system is very different and has different endpoints, authentication and necessary headers/payload structures. So, you will need to reference the API documentation of your system to adjust my examples to fit your needs.

Retrieving a Token

Usually, the first step to any REST communication is authentication. Basic authentication usually requires a passing of some form of username/password to an endpoint that will then spit back a token or other identifier to pass along in all the following calls to show they are valid and to provide user context to the calls.

Below is a quick sample function I built to retrieve a token from the example external service I am using:

function retrieveToken(host,user,password) {

    var url = host + '/api/register';
    var payload = {}
    payload.email = user;
    payload.password = password;

    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "POST";
    req.postData = Stringify(payload);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON.token;
}

The above function requires 3 passed values.

  • Host – (aka base url) In this case, it is https://reqres.in
  • User – This is the username to sign into the service
  • Password – This is the password related to the username to sign in

Once these values are passed into the function, we first form the full URL by appending '/api/register' to the passed in host. This then gives us the full target url of 'https://reqres.in/api/register'.

We then need to create the object for the payload to send to this url. This is accomplished by creating a JSON Object and then insert the above data in. A JSON Object, or JavaScript Object Notation, is a type of format for structuring data – kind of like a server-to-server version of an excel spreadsheet.

  • First we create the object: var payload = {};
  • Next we add the user: payload.email = user;
  • Finally we add the password: payload.password = password;
  • This can also be done manually like so, if preferred:
var payload = {
  "email": user,
  "password": password
}

For the example, the username is actually labeled as ’email’, which is why the name inside the payload is ’email’ instead of ‘user’.

We then send this payload in to the URL via req.send() and receive a response.

For this example, the token value is a key inside of the response Object, so all we need to do to get the token is declare resultJSON.token and then return that value.

You would call this function, like below:

var token = retrieveToken(host,user,password);

Create in External Service

Now that we have a token, how do we do anything? Well, as stated before, each service is unique, so you will need to adjust the below according to the documentation for your service – but here is a very basic example:

function createUser(host,token,payload) {

    var url = host + '/api/users';
    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "POST";
    req.setHeader("Authorization", token);
    req.postData = Stringify(payload);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON;
}

As you will notice, this is strikingly similar to the token call. This is because on the vast majority it is! The major differences are that

  • the payload is created prior to the call and passed in
  • the token is now a passed in value and user/password are removed
  • the endpoint append is different
  • this sets a header to the call to include the Authorization token
  • the return of the function is the raw JSON Object, and not a specific value of it

For this example, I built the payload Object like so:

var payload = {
  "first_name": "John",
  "last_name": "Doe",
  "avatar": "https://vignette.wikia.nocookie.net/avatar/images/8/82/Avatars.png"
}

This is then processed through the function and attached to the created URL – same as the Request Token function above.

The next important difference is the Header of the call. You will now need to pass your auth token in the calls to allow them to accept your request. To do that, you will need to utilize setHeader() on the request.

Inside of this, you will need to set the Name of the header and the Value. This is separated by a comma and cannot be bulk inserted via Arrays or Objects. It must fit the format like this:
req.setHeader('HeaderName','HeaderValue');

If you have multiple headers, you will need to use this command multiple times (Again, see my previous article for more info on the usage of Script.Util). Something like:

...
    req.method = "POST";
    req.setHeader("Authorization", token);
    req.setHeader("Environment", "SFMC");
    req.postData = Stringify(payload);
...

Once the call is sent, you will then receive the response and will be able to interact with it via the return statement as necessary.

Update in External Service

This is nearly identical to the Create function, the main difference is that the method is changed from POST to PATCH and it needs the UserID passed to be able to target the specific record.

By switching from POST to PATCH, you no longer have your call appending a record in, but instead targeting an existing record and changing the info. In order to accomplish this, you will need to include the unique identifier that is associated with that record (in this case, the UserID). Usually this is added to the end of the url for the call.

function updateUser(host,token,payload,userID) {

    var url = host + '/api/users/' + userID;
    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "PATCH";
    req.setHeader("Authorization", token);
    req.postData = Stringify(payload);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON;
}

The return of the above should be similar to that you receive from a Create call as they are very similar in structure. So you can reapply any parsing you have in Create here as well – but again, reference your services API documentation to verify. This also allows for other methods, like PUT, if those are required instead by your service.

Get from External Service

This one is slightly different from the Update, mostly just because the method is changed to GET and that there is no payload necessary.

function getUser(host,token,userID) {

    var url = host + '/api/users/' + userID;
    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "GET";
    req.setHeader("Authorization", token);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON;
}

This will then return the payload your server provides. The payload will vary from service to service, so parsing it will depend on what is returned.

As a note, Deletion is very similar to above as well, you just change GET to DELETE method.

Example Code

Putting it all together, I am going to give a sample script that will pull users from a Data Extension in SFMC that have been ‘updated’ recently, check to see if they exist inside the external service and if they do, then update, otherwise create.

<script runat=server>
Platform.Load('Core','1.1.1');

var host = 'https://reqres.in'
	user = '[email protected]',
	password = "pistol";

var de = DataExtension.Init("rest_Example");
var complexfilter = {
                LeftOperand:{
                    Property:"LastModified",
                    SimpleOperator:"greaterThan",
                    Value: '06/22/2020'
                },
                LogicalOperator:"AND",
                RightOperand:{
                    Property:"Status",
                    SimpleOperator:"equals",
                    Value:"Active"
        }};

var deArr = de.Rows.Retrieve(complexfilter);

var token = retrieveToken(host,user,password);

for (i=0; i<deArr.length;i++) {
    var userID = deArr[i].UserID
	var getUserObj = getUser(host,token,userID);
  
    var payload = {}
    payload.first_name = deArr[i].first_name;
    payload.last_name = deArr[i].last_name;
    payload.avatar = deArr[i].avatar;

    if(getUserObj.length > 1) {
    	var updateUserResp = updateUser(host,token,payload,userID);
	} else {
    	var createUserResp = createUser(host,token,payload);
	}
}

function retrieveToken(host,user,password) {

    var url = host + '/api/register';
    var payload = {}
    payload.email = user;
    payload.password = password;

    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "POST";
    req.postData = Stringify(payload);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON.token;
}

function getUser(host,token,userID) {

    var url = host + '/api/users/' + userID;
    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "GET";
    req.setHeader("Authorization", token);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON;
}

function createUser(host,token,payload) {

    var url = host + '/api/users';
    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "POST";
    req.setHeader("Authorization", token);
    req.postData = Stringify(payload);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON;
}

function updateUser(host,token,payload,userID) {

    var url = host + '/api/users/' + userID;
    var req = new Script.Util.HttpRequest(url);
    req.emptyContentHandling = 0;
    req.retries = 2;
    req.continueOnError = true;
    req.contentType = "application/json"
    req.method = "PATCH";
    req.setHeader("Authorization", token);
    req.postData = Stringify(payload);

    var resp = req.send();

    var resultStr = String(resp.content);
    var resultJSON = Platform.Function.ParseJSON(String(resp.content));

    return resultJSON;
}

</script>

Hopefully this should help provide a boilerplate for reference in your integration needs from inside of SFMC SSJS. Please feel free to leave comments or submit questions in my contact form if I missed something or did not fully explain something. Thank you for reading!

Tags: , , , , , , , , , , , ,
Subscribe
Notify of
guest
4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Amy
Amy
3 years ago

Hi – Thank you for the detailed post. I am trying to use the same to do a POST to an external service. i am having trouble getting the response to display in SFMC. When I click on the URL of the cloud page it display blank. Am I missing something?

Amy Nayak
Amy Nayak
Reply to  Gortonington
3 years ago

join

Amy Nayak
Amy Nayak
Reply to  Gortonington
3 years ago

Attached is the script I have used

SSJS_script.jpg