How to use Content Builder API in Salesforce Marketing Cloud

How to use Content Builder API in Salesforce Marketing Cloud

This article showcases how to use Content Builder API in Salesforce Marketing Cloud.

Before we start

First and foremost, we need to define what Content Builder actually is and what kind of API (application programming interface) it uses.

Content Builder is what we call a DAM (Digital Asset Management) integrated as one of the core applications in Salesforce Marketing Cloud for cross-channel asset management.

Content Builder API, however, refers to the REST API implementation in Marketing Cloud that targets specifically the assets (images, documents, emails, content blocks, etc…).

Therefore, the following code snippets and recommendations concern exclusively the use of REST API for the Content Builder and its many asset types.

HTTP requests

HTTP requests are the bread and butter of REST API. They allow us to communicate with the Content Builder and perform the CRUD operations.

Server-side JavaScript methods

In server-side JavaScript, there are 2 possible methods of performing HTTP requests: Core and Script Utility Object.

Core example

var request = HTTP.Post(
	endpoint, 
	"application/json", 
	Stringify(payload), 
	["Authorization"], 
	["Bearer " + accessToken]
);

var result = request.Response;

Script Utility Object example

var request = new Script.Util.HttpRequest(endpoint);

	request.emptyContentHandling = 0;
	request.retries = 2;
	request.continueOnError = true;
	request.setHeader("Authorization", "Bearer " + accessToken);
	request.method = "POST";
	request.contentType = "application/json";
	request.postData = Stringify(payload);

var results = request.send();

var status = results.statusCode;

var result = Platform.Function.ParseJSON(String(results.content));

Choosing one or the other depends on the requirements of our code.

Core method is shorter to write, fast to remember and the response object doesn’t require parsing, but it only provides POST and GET request methods and lacks the flexibility of defining the number of retries, encoding or providing a better error handling.

Moreover, performing POST and GET requests with the Core method will return the results in a different sub-object:

HTTP.Get() response

{
	Status: 0,
	Content: {
		...
	}
}

HTTP.Post() response

{
	Status: 0,
	Response: {
		...
	}
}

As for the Status property, here are all the possible values returned by the Core method:

ValueMeaning
0Status is OK
-1Missing URL
-2HTTP request error
-3No content returned

POST and GET requests

Now that we learned about the methods of HTTP requests in server-side JavaScript, let’s have a closer look at the different types of requests and their caveats.

POST requests

POST requests, generally speaking, are a type of the HTTP requests, used to create a new resource.

For example, to create a new folder or upload a new image.

But, in Marketing Cloud, a POST request can also perform an advanced search! Something that is traditionally done with a GET request.

Example

var payload = {
    page: {
        page: 1,
        pageSize: 50
    },
    query: {
        property: "assetType.id",
        simpleOperator: "IN",
        value: ["20", "21", "22", "23", "28"]
    },
    sort: [
        {
            property: "id",
            direction: "ASC"
        }
    ],
    fields: [
        "thumbnail",
        "category",
        "content",
        "data"
    ]
}

var endpoint = restInstanceUrl + "asset/v1/content/assets/query";

var request = HTTP.Post(
	endpoint, 
	"application/json", 
	Stringify(payload), 
	["Authorization"], 
	["Bearer " + accessToken]
);

var result = request.Response;

Note that passing the parameters in the request is matter of compiling the data in a well organised object and passing it as a string to the HTTP method.

In the example above, the query filter is based on the asset ids (read full list), which exist for every type of asset in the Content Builder.

Here is the id mapping for the most used web image formats.

IdName
20gif
21jpe
22jpeg
23jpg
28png

GET requests

GET requests, on the other hand, do exactly as designed: retrieve assets from the Content Builder!

But Marketing Cloud wouldn’t be Marketing Cloud if there wasn’t a catch: passing the parameters in these requests is somewhat unorthodox and require a specific syntax.

Traditionally, adding parameters to a GET request is a matter of concatenating them at the end of the endpoint, in the same manner as we would do for the URL parameters.

https://mydomain.com/api/post?size=50&order=asc

Content Builder API, on the other hand, prefixes each parameter with a $ symbol and uses keywords, simple quotes and spaces to create filters.

GET parameters

$pageNumber of the page to retrieve. Starts with 1.
$pagesizeNumber of results to return by request.
$orderByName of the field and the direction of sorting.
$fieldsComma-separated field names to include in the response.
$filterSimple condition for filtering the result of the query.

GET filter operators

eqEquals. Compares numbers and strings.
neqIs not equal to. Compares numbers and strings.
ltIs less than. Compares numbers only.
lteIs less than or equal to. Compares numbers only.
gtIs greater than. Compares numbers only.
gteIs greater than or equal to. Compares numbers only.
likeIs similar to. Compares strings only.

Examples

/asset/v1/content/categories?$pagesize=20&$filter=parentId eq 3916

/asset/v1/content/assets?$filter=Name%20like%20'hello%20world'

Note that both empty spaces and URL encoded space %20 both work, but it’s best practice to always encode special characters in the URL.

GET responses

What about the responses? Are there any caveats? Of course there are!

In Content Builder API, GET requests return an parsable object in 99% of cases (as far as I know), except for the file binary retrieval.

Request

var id = "123456";

var endpoint = restInstanceUrl + "asset/v1/content/assets/" + id + "/file";

var request = HTTP.Get(endpoint, ["Authorization"], ["Bearer " + accessToken]);

var result = request.Content;

Response

"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACB..."

Why? Because of reasons!

Note that when using Script Utility Object to perform the request, there is no longer any need to parse the JSON object from the response:

Platform.Function.ParseJSON(String(results.content))

PATCH and PUT requests

In a regular API, PATCH requests are used to partially update a resource and a PUT request replaces the target completely.

In Marketing Cloud, again, not everything is by the book.

Sending PATCH requests to update assets is a breeze, but for whatever reason, updating the category of an asset (folder) requires the usage of the PUT request.

var id = 123456;

var payload = {
    Id: 123456,
    Name: "Updated Folder Name",
    ParentId: 78912345,
    CategoryType: "asset"
};

var endpoint = restInstanceUrl + "asset/v1/content/categories/" + id;

var request = new Script.Util.HttpRequest(endpoint);
    request.emptyContentHandling = 0;
    request.retries = 2;
    request.continueOnError = true;
    request.setHeader("Authorization", "Bearer " + accessToken);
    request.method = "PUT";
    request.contentType = "application/json";
    request.postData = Stringify(payload);

var results = request.send();

var result = Platform.Function.ParseJSON(String(results.content));

DELETE request

And finally, let’s talk about the most dangerous API method: DELETE request.

When performing this request using the asset ID, the deletion from the Content Builder is permanent. Or is it?

The short answer is: NOT!

In order to permanently delete the asset, we need to provide an extra parameter at the and of the endpoint: isCDNDelete=1

var id = "123456";

var endpoint = restInstanceUrl + "asset/v1/content/assets/" + id + "?isCdnDelete=1";

var request = new Script.Util.HttpRequest(endpoint);
    request.emptyContentHandling = 0;
    request.retries = 2;
    request.continueOnError = true;
    request.setHeader("Authorization", "Bearer " + accessToken);
    request.method = "DELETE";
    request.contentType = "application/json";

var results = request.send();

var result = results.statusCode;

What happens if we don’t? The public URL of the asset remains available and accessible to the users (official documentation).

This, of course, only concerns the assets with public URLs, such as images and documents. As for the blocks and such, they are gone forever!

If you want to avoid permanently deleting files, please use the archive endpoint.

Conclusion

Using Content Builder API is a challenging endeavour. Hopefully this article helps you out on your Marketing Cloud journey of discovery and provides a much needed explanation where the documentation is lacking.

Want to learn more REST API methods? Jump to my new documentation website!

Good luck out there and be golden!

Have I missed anything?

Please poke me with a sharp comment below or use the contact form.

server-side Javascript
Up Next:

I don't use getNextBatch and neither should you

I don't use getNextBatch and neither should you