Creating Templates using the API

Creating Templates

With PostGrid, all mails require that you either supply a PDF or a template to form the basis of the mail. Templates allow you to dynamically create mails based upon the specifics of a given contact or any other information. For an easy option of creating templates, you can check out our Using the Template Editor on using PostGrid's built-in visual editor. When actually sending the mail, you only need to use the template's ID which can be found in the dashboard.

Templates as HTML

Templates consist of a description and some HTML to describe the template itself. There really aren't too many limits as to what you can do with the HTML you supply the API as long as it is contained in a single HTML file. This would require that any styling must either be done inline as

<div style="color:red">

or by including style tags within the <head> such as

<head>
	<style>
		.narrow: {
			width: 19px
		}
	</style>
</head>

Keep in mind that this will be converted to print, so use the print preview feature of your browser for an idea as to how it will turn out. PDF previews of actual mail can be found after creation of the mail. There are some other specifications to abide by, and these will be covered in a section titled 'Formatting Concerns' present within the respective guides of letters, postcards or cheques.

Adding Variables to Templates

One of the most useful features of templates is the ability to add variable information. To add variable data to a template, simply add a text element and write {{variable_name}}, where variable_name can be whatever you wish. There are also some preset variables which fill-in based on sender and receiver information such as {{from.firstName}}. Variables may be easily inserted into the HTML such as

<body>
    <p> Dear {{to.firstName}} </p>

    <p> Welcome to our company! </p>

    <p> Sincerely, {{from.firstName}} </p>
<body>

Variables may also be used to control styling of HTML elements in this way. such as setting

<p style="color: {{section_color}}">

During the template creation phase, you only need to worry about adding in which data may vary from mail to mail as you use the template while PostGrid helps you to take care of the rest.

To know which variables can be filled in from contact information, take a look at the details of a contact to see what information is available. Opening up the 'Raw Data', here is an example below.

1768

Variables with nested information can be accessed using variable.nestedInfo, such as specifying {{income.afterTax}} to access the income after tax which may be stored as

{
	"income" : {
		"beforeTax" : "100000",
		"afterTax" : "73000"
	}
}

More information on variables can be found below.

Template Creation

With our HTML prepared, we are ready to actually make our request to the server and create our very first template. Don't forget to retrieve your API key from the 'settings' section of the dashboard for this section!

For creating a template, we find in the documentation that our endpoint is to make a POST request to /templates. In the body of our request, we are going to need to supply the HTML to be used by the template as well as the description we will use to find our template later. In our JavaScript file, let's add the following.

// Replace this with the contents of an HTML file,
// below is only an example.
const templateHTML = `
<html>
	<body>
		<h1> Dear {{to.firstName}} </h1>

		<p> I hope that this letter finds you well. Here is the new policy ... </p>

		<p> Sincerely, </p>
		<p> {{from.firstName}} </p>
		<p> {{from.jobTitle}} </p>
		<p> {{from.companyName}} </p>
	</body>
</html>
`;

const templateDescription = 'This is a test Template' // Insert your description here

With our description and HTML supplied, let's create a function which consumes these two values and produces the server's response.

const fetch = require('node-fetch');

const POSTGRID_URL = 'https://api.postgrid.com/print-mail/v1';

const API_KEY = '' // Place your API key here

/**
 * @param {string} templateDescription
 * @param {string} templateHTML
 */
async function createTemplate(templateDescription, templateHTML) {
    const requestOptions = {
        method: 'POST',
        body: JSON.stringify({
            description: templateDescription,
            html: templateHTML,
        }),
        headers: {
            'x-api-key': API_KEY,
            'Content-Type': 'application/json',
        },
    };

    const resp = await fetch(POSTGRID_URL + '/templates', requestOptions);

    return await resp.json();
}

With everything set, let's run our command node -i -e "$(< app.js)" to load everything into the REPL. Running our function in the Node REPL and logging it use our function logAwait by running logAwait(createTemplate(templateHTML, templateDescription)), we should receive something along the following.

{
    "id": "template_suknWmGvcjFeQyW6iUsBou",
    "object": "template",
    "live": false,
    "description": "Test",
    "html": "<html>\n\t\t<body>\n\t\t\t<h1> Dear {{to.firstName}} </h1>\n\n\t\t\t<p> I hope that this letter finds you well. Here is the new policy ... </p>\n\n\t\t\t<p> Sincerely, </p>\n\t\t\t<p> {{from.firstName}} </p>\n\t\t\t<p> {{from.jobTitle}} </p>\n\t\t\t<p> {{from.companyName}} </p>\n\t\t</body>\n\t</html>",
    "createdAt": "2021-09-01T20:13:01.877Z",
    "updatedAt": "2021-09-01T20:13:01.877Z"
}

If an error occurred, the response resp would contain the appropriate status code resp.status, while the response object would contain a field error which breaks down into error.type and error.message for more info. Below is an example of a faulty API key with status code 401 (unauthorized).

{
    "error": {
        "type": "invalid_api_key_error",
        "message": "Invalid API key test_sk_jqBwg2TCuEBFezMHTJPF5"
    }
}

Viewing and Retrieving Templates

If we now wanted to view our template, we will now be looking to make a GET request to URL /templates/:id, where :id can be found in the "id" section found in the dashboard or the response we obtained above. If we wanted to create a function which would retrieve the template with a given id, we could write something similar to the following.

/**
 * @param {string} id
 */
async function retrieveTemplate(id) {
	const requestOptions = {
		method: 'GET',
		headers: {
			'x-api-key': API_KEY
		}
	};

	const resp = await fetch(POSTGRID_URL + `/templates/${id}`, requestOptions);

	return await resp.json();
}

Running the above code, we would expect a response as below.

{
    "id": "template_suknWmGvcjFeQyW6iUsBou",
    "object": "template",
    "live": false,
    "description": "Test",
    "html": "<html>\n\t\t<body>\n\t\t\t<h1> Dear {{to.firstName}} </h1>\n\n\t\t\t<p> I hope that this letter finds you well. Here is the new policy ... </p>\n\n\t\t\t<p> Sincerely, </p>\n\t\t\t<p> {{from.firstName}} </p>\n\t\t\t<p> {{from.jobTitle}} </p>\n\t\t\t<p> {{from.companyName}} </p>\n\t\t</body>\n\t</html>",
    "createdAt": "2021-09-01T20:13:01.877Z",
    "updatedAt": "2021-09-01T20:13:01.877Z"
}

If we would like to be able to find this template by searching for its description, we need to make a POST request to /templates, being sure to fill out the search option in the body. For this, let's create a function which consumes a search criteria and returns the server's response.

/**
 * @param {string} search
 */
async function searchTemplates(search) {
    const requestOptions = {
        method: 'GET',
        headers: {
            'x-api-key': API_KEY,
        },
    };

    const queryString = `?skip=0&limit=10&search=${search}`;

    const resp = await fetch(
        POSTGRID_URL + '/templates' + queryString,
        requestOptions
    );

    return await resp.json();
}

Using the REPL and running logAwait(searchTemplates('This is a test template')), we may receive something like the following

{
    "object": "list",
    "limit": 10,
    "skip": 0,
    "totalCount": 1,
    "data": [
        {
            "id": "template_suknWmGvcjFeQyW6iUsBou",
            "object": "template",
            "live": false,
            "description": "This is a test Template",
            "html": "\n<html>\n\t<body>\n\t\t<h1> Dear {{to.firstName}} </h1>\n\n\t\t<p> I hope that this letter finds you well. Here is the new policy ... </p>\n\n\t\t<p> Sincerely, </p>\n\t\t<p> {{from.firstName}} </p>\n\t\t<p> {{from.jobTitle}} </p>\n\t\t<p> {{from.companyName}} </p>\n\t</body>\n</html>\n",
            "createdAt": "2021-09-01T20:13:01.877Z",
            "updatedAt": "2021-09-01T20:13:01.877Z"
        }
    ]
}

This method of search works well when you have something inexact you are looking for. Additionally, PostGrid's search feature also comes with the ability to parse exact matches by passing in an object which resembles a template where the specified fields will be used in the search for an exact match. Below, we have an example of searching for an exact description. Careful to handle the error with no matching description!

/**
 * @param {string} description
 */
async function retrieveTemplateID(description) {
    const searchString = encodeURIComponent(
        JSON.stringify({
            description: description,
        })
    );

    const searchResults = await searchTemplates(searchString);

    if (searchResults.totalCount > 0) {
        return searchResults.data[0].id;
    }

    throw new Error('Description not found');
}

Note that we must use encodeURIComponent to translate our object string into a URI compatible string. We could add more fields to produce more exact matches, which could be particularly handy if you added a metadata object to your template when uploading as this would allow you to search along all sorts of parameters such as campaignID, clientName, or anything else specified.

Updating Templates

Using a template's ID, it is also very easy to update either the HTML or the description of the template. In the above example, notice that we capitalized the 'T' in 'Template', so let's fix this. We will begin by constructing a function which consumes a template ID and some options to revise. Following the API documentation, we will be making a POST request to /templates/:id.

/**
 * @param {string} id
 * @param {{description?: string, html?: string}} options
 */
async function editTemplate(id, options) {
    const requestOptions = {
        method: 'POST',
        headers: {
            'x-api-key': API_KEY,
        },
        body: JSON.stringify({
            description: options.description,
            html: options.html,
        }),
    };

    const resp = await fetch(POSTGRID_URL + `templates/${id}`, requestOptions);

    return await resp.json();
}

Running the above function in the REPL and logging the result will produce the following when we correct our error.

{
    "id": "template_suknWmGvcjFeQyW6iUsBou",
    "object": "template",
    "live": false,
    "description": "This is a test template",
    "html": "\n<html>\n\t<body>\n\t\t<h1> Dear {{to.firstName}} </h1>\n\n\t\t<p> I hope that this letter finds you well. Here is the new policy ... </p>\n\n\t\t<p> Sincerely, </p>\n\t\t<p> {{from.firstName}} </p>\n\t\t<p> {{from.jobTitle}} </p>\n\t\t<p> {{from.companyName}} </p>\n\t</body>\n</html>\n",
    "createdAt": "2021-09-03T16:24:20.275Z",
    "updatedAt": "2021-09-03T21:22:34.689Z"
}

Deleting Templates

Finally, we may also want to remove our templates entirely. This is as simple as running a DELETE request to /templates/:id. Using this function,

/**
 * @param {string} id
 */
async function deleteTemplate(id) {
    const requestOptions = {
        method: 'DELETE',
        headers: {
            'x-api-key': API_KEY,
        },
    };

    const resp = await fetch(POSTGRID_URL + `templates/${id}`, requestOptions);

    return await resp.json();
}

we produce the following upon first deletion,

{
    "id": "template_suknWmGvcjFeQyW6iUsBou",
    "object": "template",
    "deleted": true
}

and after attempting again, we will receive the following:

{
    "error": {
        "type": "template_not_found_error",
        "message": "Could not find template with ID 'template_suknWmGvcjFeQyW6iUsBou'"
    }
}