When your business requires sending mail in an effective and scalable manner, PostGrid has your solution. In this guide, we will examine how to send cheques using the PostGrid Print & Mail API. For more information on how to use the API, be sure to consult the official documentation.

Overview

There are six stages in the process of sending cheques with PostGrid:

  • Creating contacts
  • Creating bank accounts
  • Creating templates for optional letter
  • Sending cheques
  • Tracking cheques and other analytics

Whether you have a small operation with no developers or a large operation with engineers on hand, each of these four stages have tools at a variety of different levels to accommodate. In this guide, we aim to show you how you can use our API to accomplish each of these four stages and how you can safely test out your workflow. Due to the flexibility of the API endpoints, we may actually skip the setup steps of creating templates and creating contacts but this guide will cover how to use the features as well since it generally creates good practice to have the templates and contacts easily accessible even after the mail is sent.

Live and Test Modes

To make sure that your customers are receiving the exact postcard you wish to intend, PostGrid offers both a test mode and a live mode. In test mode, actual postcards are not sent out but the process of creating orders is completed all the same. This allows you to ensure that variable data is assigned properly and when you preview a postcard, it adheres to postal standards and looks exactly as it should. When you are ready to actually send the postcards, you can switch to live mode and repeat the sending process with everything set up as before. Postcards sent in test mode will not be delivered, even after switching to live mode. More details on these features will be discussed in the tracking section.

API Keys

For developers following along wishing to use the API, you will need to know where to locate and how to use your API keys. The keys can be found in your settings on the dashboard, featuring a live key prefixed by live_sk, and a test key prefixed by test_sk. The live key can be used to carry out actual mail orders while the test key will only send out postcards in test mode. More details can be found here, in the official API documentation. With each request, be sure to include the x-api-key header with your API key as the value.

Environment and Setup

In this guide, we will be using Node.js as well as the module node-fetch to make our API calls and make it easy to understand how this can be used in the browser as well. For alternative language support, see the official documentation as well. If you have any questions concerning the JavaScript Fetch API, a great resource is the MDN Web Docs. To install this module, simply run

npm install node-fetch

Next, create a new file which we will call app.js. As we will be focused on making primarily asynchronous API calls, let's add some helper functions and constants throughout this document.

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

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

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

/**
 * @param {Promise<any>} x
 */
async function logAwait(x) {
    try {
        console.log(await x);
    } catch (e) {
        console.error(e);
    }
}

To only call the specific functions we want, we will be using the Node.js REPL by running the command

node -i -e "$(< app.js)"

or equivalently on Windows PowerShell

node -i -e "$(type app.js)"

which will load our functions into the REPL and can be called from there. If you name your file something else, be sure to replace app.js in the above.

In a Windows command terminal, loading files into the REPL is nowhere near as convenient but can be done if we run node to open the REPL and then run the command .load app.js. This will populate the terminal with the contents of your file line-by-line and may take a short period of time to load. As the file grows in scale, you may wish to include any logging at the end of your file as logAwait(someAsyncFunction()) and run the script in the usual way as node app.js.

Also, as you can see from the function logAwait, we will be adding annotations to our code to explain what you can expect from each of the parameters being used. For more information, be sure to have a look at JSDoc. If you are familiar with TypeScript, these annotations can be seen as a way to add typing to a JS file without using TypeScript syntax. These annotations will also allow us easy access to autocomplete within our code.

Creating Contacts

Before we can get started with sending cheques, we will need to specify who the cheques are from and who they are going to. Although contact creation may be easily done using the dashboard, PostGrid still offers the ability to create and view contacts directly from the API. For an in-depth guide on how to create contacts using the API, read this guide.

Creating Bank Accounts

Before we can get started with sending cheques, we will need to specify which bank account the sender uses to transfer money. Although bank account creation may be easily done using the dashboard, PostGrid still offers the ability to create and view bank accounts directly from the API.

Making New Bank Accounts

Before we can make a new bank account, we need to know what information a bank account consists of.

Using the above image, we have a guide as to what type of information we will need to send in order to create a bank account. In the above list, every field except for 'id', 'object', 'live' are specified upon creation of a bank account.

To keep things simple, let's create a function to create a bank account with a certain bank name, country code, account number, transit number and route number. Looking at our API documentation, we need to make a POST request to /bank_accounts, so let's do so.

The following function creates a Canadian bank account using the PostGrid Print & Mail API.

/**
 * @param {string} bankName
 * @param {number} accountNumber
 * @param {number} transitNumber
 * @param {string} routeNumber
 * @param {string} countryCode
 * @param {string} signatureText
 * @param {string} description
 * @param {string} bankPrimaryLine
 */
async function createCanadianBankAccount(bankName, accountNumber, transitNumber, routeNumber, countryCode, signatureText, description, bankPrimaryLine) {
    const requestOptions = {
        method: 'POST',
        headers: {
            'x-api-key': API_KEY,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            bankName : bankName,
            accountNumber: accountNumber,
            transitNumber:transitNumber,
            routeNumber: routeNumber,
            bankCountryCode: countryCode,
            signatureText: signatureText,
            description: description,
            bankPrimaryLine: bankPrimaryLine
        }),
    };

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

    return await resp.json();
}

The bankCountryCode passed to the above function should be 'CA' as we are creating a Canadian bank account.
Signature Text auto-generates a signature that will be used for documents based on the string provided. You can also replace it with an image of your signature using the signatureImage field.

An example of what we receive from the above function is given below.

{
    "id": "bank_pRfyyrHpgQKW2Ptx2pZvsd",
    "object": "bank_account",
    "live": false,
    "accountNumberLast4": 4567,
    "bankCountryCode": "CA",
    "bankName": "CIBC",
    "bankPrimaryLine": "77 Bloor St W",
    "bankSecondaryLine": "",
    "description": "This bank account will be used to send cheques",
    "routeNumber": "010",
    "signatureText": "David",
    "transitNumber": 12345,
    "createdAt": "	2022-05-18T12:50:59.257Z",
    "updatedAt": "2022-05-18T12:50:59.257Z"
}

The following function creates an American bank account using the PostGrid Print & Mail API.

/**
 * @param {string} bankName
 * @param {number} accountNumber
 * @param {number} routingNumber
 * @param {string} countryCode
 * @param {string} signatureText
 * @param {string} description
 * @param {string} bankPrimaryLine
 */
async function createAmericanBankAccount(bankName, accountNumber, routingNumber, countryCode, signatureText, description, bankPrimaryLine) {
    const requestOptions = {
        method: 'POST',
        headers: {
            'x-api-key': API_KEY,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            bankName : bankName,
            accountNumber: accountNumber,
            routingNumber: routingNumber,
            bankCountryCode: countryCode,
            signatureText: signatureText,
            description: description,
            bankPrimaryLine: bankPrimaryLine
        }),
    };

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

    return await resp.json();
}

The bankCountryCode passed to the above function should be 'US' as we are creating an American bank account.
Signature Text auto-generates a signature that will be used for documents based on the string provided. You can also replace it with an image of your signature using the signatureImage field.

Viewing Bank Accounts

As with contacts, we have very similar options for viewing bank accounts. We may either examine an individual contact by sending a GET request to /bank_accounts/:id, or list contacts by sending a GET request to /bank_accounts. As these work so similarly to their equivalent requests to /contacts and /templates, we will only show a brief function for retrieving bank account information.

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

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

	return await resp.json();
}

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

{
  "id": "bank_mycTFJcd2d5SHifVHidiwc",
  "object": "bank_account",
  "live": false,
  "accountNumberLast4": "3211",
  "bankCountryCode": "CA",
  "bankName": "Example Bank",
  "bankPrimaryLine": "100 Garden Street",
  "bankSecondaryLine": "Gananoque, ON K7G 1H9",
  "routeNumber": "678",
  "transitNumber": "12345",
  "createdAt": "2020-11-12T08:47:01.880Z",
  "updatedAt": "2020-11-12T08:47:01.880Z"
}

Creating Templates for Optional Letter

With PostGrid, cheques allow you to attach an optional letter. All letters require that you either supply a PDF or a template to form the basis of the letter. Templates allow you to dynamically create letters based upon the specifics of a given contact or any other information. For an in-depth guide on how to create templates using the API, read this guide. For an easy option of creating templates, you can check out our Template Editor Guide ) on using PostGrid's built-in visual editor. When actually sending the letter, you only need to use the template's ID which can be found in the dashboard.

Using PDFs

If you don't need to create a template but instead have PDFs already ready to send to specific contacts, then you can move right along to the sending part. However, you will still need to read the following section to be sure of the format of the PDF letter you wish to send.

Formatting Concerns

When sending and printing mail, we need to keep in mind where the cheque will be placed as well as where the margins can be found. The cheque is placed 7.6" from the top on a page of size 8.5" x 11". The page margins are located on 0.3" on the sides and 0.35" on the top to account for the message. Any additional pages, including those for an attached letter, are double-sided and are printed in black & white.

In the official API documentation (here), you can find the specifics for Canadian letters and for US & international letters. To avoid having the address cover a specific part of your optional letter.

Sending Cheques

With our contacts, bank account and letter templates setup, we are ready to send some (test) mail. In this section, we will cover each of the different ways you can send mail by making API calls. Over these sections, be sure to use the test key when following along to make sure that you do not send any real mail.

Sending Cheques without Letters to Contacts

The quickest and easiest way to send mail is if you have the contact IDs and bank acccount ID for the mail you wish to send. Examining the documentation, we can achieve this with a simple POST call to /cheques. Let's now create a function to consume some contact IDs and a bank account ID and create a cheque.

/**
 * @param {string} toContactID
 * @param {string} fromContactID
 * @param {string} bankAccountID
 * @param {string} cents
 */
async function createCheque(toContactID, fromContactID, bankAccountID, cents) {
    const requestOptions = {
        method: 'POST',
        headers: {
            'x-api-key': API_KEY,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            to: toContactID,
            from: fromContactID,
            bankAccount: bankAccountID,
            amount: cents
        }),
    };

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

    return await resp.json();
}

It is important to note that the amount specified must be in cents.

Sending Cheques with Letters to Contacts

/**
 * @param {string} toContactID
 * @param {string} fromContactID
 * @param {string} bankAccountID
 * @param {string} cents
 * @param {string} templateID
 */
async function createChequeWithLetterTemplate(toContactID, fromContactID, bankAccountID, cents, templateID) {
    const requestOptions = {
        method: 'POST',
        headers: {
            'x-api-key': API_KEY,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            to: toContactID,
            from: fromContactID,
            bankAccount: bankAccountID,
            amount: cents,
            letterTemplate: templateID
        }),
    };

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

    return await resp.json();
}

The letter attached will be black-and-white, double-sided, and will be inserted into the same envelope as the cheque.

To create letter templates with pure HTML, you can replace the letterTemplate field with the letterHTML field which requires the HTML content of the letter as a string.

You can also create a cheque with an attached letter using the letter's PDF. This can be done by replacing the letterTemplate field with the letterPDF field which requires either a PDF file or the publicly accessible link of your PDF to be passed.

Tracking Cheques

With your mail sent, it is now time to track and view your cheques to make sure that everything is in order and that everything has been sent successfully.

Tracking an Individual Cheque

Our first task will be to retrieve a singular cheque. For this section, it will be important that you have kept the ID of your cheque which was returned by the API when it was created in the previous section. If you did not record it, or would not like to save this information, we will go over the search functionality in the next section to see how you may still receive the information on your cheques without saving the ID. In order to obtain the information on our individual cheque, we will need to send a GET request to /cheques/:id. Let's design a very basic function to perform exactly this.

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

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

    return await resp.json();
}

Let's pass the output of this function through logAwait to see what we get.

{
  "id": "cheque_mK8PfefU41ZQnKBRCaUayh",
  "object": "cheque",
  "live": false,
  "amount": 10000,
  "bankAccount": {
    "id": "bank_3yXGjQ6ee4pHkLhb3a7keu",
    "object": "bank_account",
    "live": false,
    "accountNumberLast4": "3211",
    "bankCountryCode": "CA",
    "bankName": "CIBC",
    "bankPrimaryLine": "100 Bank Street",
    "bankSecondaryLine": "Toronto, ON M9V4V1",
    "description": "Example Bank Account",
    "routeNumber": "123",
    "transitNumber": "12345",
    "createdAt": "2020-11-13T08:28:42.509Z",
    "updatedAt": "2020-11-13T08:28:42.509Z"
  },
  "description": "Test",
  "from": {
    "id": "contact_jkTtb6ovKZ7xXe7N5rXipX",
    "object": "contact",
    "addressLine1": "20-20 BAY ST",
    "addressLine2": "FLOOR 11",
    "addressStatus": "verified",
    "city": "TORONTO",
    "companyName": "PostGrid",
    "country": null,
    "countryCode": null,
    "postalOrZip": "M5V 4G9",
    "provinceOrState": "ON"
  },
  "memo": "A short memo.",
  "number": 5049,
  "sendDate": "2020-11-18",
  "status": "ready",
  "to": {
    "id": "contact_jkTtb6ovKZ7xXe7N5rXipX",
    "object": "contact",
    "addressLine1": "20-20 BAY ST",
    "addressLine2": "FLOOR 11",
    "addressStatus": "verified",
    "city": "TORONTO",
    "companyName": "PostGrid",
    "country": null,
    "countryCode": null,
    "postalOrZip": "M5V 4G9",
    "provinceOrState": "ON"
  },
  "createdAt": "2020-11-18T05:10:52.994Z",
  "updatedAt": "2020-11-18T05:10:52.994Z"
}

In the above, we see that we have access to all of the information of the cheque including information regarding the contacts, bank account, amount, and many more. Discussed further in the Cheque Progress section will be the field status, and in the 'Previewing Cheques' section, we will be interested in the field url.

Tracking Multiple Cheques

In addition to an endpoint for retrieving exactly one cheque to view, PostGrid also offers the ability to list cheques to analyze multiple at once.
Retrieving multiple cheques will require a GET request to be sent to /cheques, where we will default our query parameters to skip=0.

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

    const queryParams = `?skip=${skip}`;

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

    return await resp.json();
}

Using the above, let's take a search with the string '0' to see what the response object will look like.

{
  "object": "list",
  "limit": 10,
  "skip": 0,
  "totalCount": 1,
  "data": [
    {
      "id": "cheque_dYwJ1uc428rg1pwzqcRirV",
      "object": "cheque",
      "live": false,
      "amount": 10000,
      "bankAccount": "bank_mycTFJcd2d5SHifVHidiwc",
      "description": "Test",
      "from": {
        "id": "contact_2ebY285LeamvfBZrJTdCvP",
        "object": "contact",
        "addressLine1": "20-20 BAY ST FLOOR 11",
        "addressLine2": "",
        "addressStatus": "verified",
        "city": "TORONTO",
        "companyName": "PostGrid",
        "country": "CANADA",
        "countryCode": "CA",
        "postalOrZip": "M5J2N8",
        "provinceOrState": "ON"
      },
      "memo": "A short memo.",
      "number": 5049,
      "sendDate": "Thu Nov 12 2020",
      "status": "ready",
      "to": {
        "id": "contact_sUeaxGua7LgDfwGcBEBfz1",
        "object": "contact",
        "addressLine1": "20-20 BAY ST FLOOR 11",
        "addressLine2": "",
        "addressStatus": "verified",
        "city": "TORONTO",
        "companyName": "PostGrid",
        "country": "CANADA",
        "countryCode": "CA",
        "postalOrZip": "M5J2N8",
        "provinceOrState": "ON"
      },
      "createdAt": "2020-11-12T22:22:47.819Z",
      "updatedAt": "2020-11-12T22:22:47.819Z"
    }
  ]
}

we can see the general response to a cheques list request. In the totalCount field, the number there will be the total amount of results in your search.
Contained in the data field will actually be a list of all the cheques matching your search in the range determined by skip.

Cheque Progress

In the above return values to our API calls, notice that we have a field status. This value can be one of:

  • ready
    • Upon successful creation, our cheque is ready and will be sent off to printers in the next business day.
  • printing
    • The next stage is printing. If you look where the 'Progress' button is, we now find that the 'Cancel' button has been disabled. At this stage, PostGrid cannot guarantee that printers have not already sent out your mail for delivery and the cheque cannot be cancelled. Fortunately, PostGrid has recognized that we are in test mode and has left us a dialogue below the stage number telling us that no actual mail was sent.
  • processed_for_delivery
    • Next, printers will take your mail and have it processed. When the local postal service has entered your mail into their systems, PostGrid will be able to relay to you that your mail has been processed and is now awaiting delivery.
  • completed
    • Finally, your cheque will have reached the status of completed. This is based on regional estimates of time to completion and not an actual verification of delivery. If you would like to get a tracking number for this mail to get an exact notification of delivery, you can send it as certified mail. For more information on how to send certified mail, see our API Guide .
  • cancelled
    • Before cheques are sent out to printers, PostGrid offers the ability to cancel your mail. Cheques sent from PostGrid will not be sent to printers until after midnight in Toronto. During this period, simply press the "Cancel" button and confirm your choice in order to prevent the cheque from being sent.

When working with test cheques, you can actually force a cheque to go through these steps. To perform this, simply make a POST request to /cheques/:id/progressions.

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

    const resp = await fetch(
        POSTGRID_URL + `/cheques/${chequeID}/progressions`,
        requestOptions
    );

    return await resp.json();
}

Using the function above, progressing a cheque will update the status and will return back the cheque object with the updated status. Progressing a completed cheque will leave you with an error message, as will progressing a non-existent cheque, a live cheque, or a cancelled cheque.

Cancelling Cheques

Before a cheque reaches the status of printing, PostGrid offers you the ability to cancel your mail. Be aware that mail that has reached the status of printing has been sent to our printers and could already be out for delivery, so before this is the last time we can guarantee that your cheques will not be sent. To cancel cheques with the API, all you need to do is make a DELETE request to /cheques/:id. Below is a simple function which consumes a cheque ID and cancels the order.

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

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

    return await resp.json();
}

Upon a successful cancellation, you should receive the same response as if you had made a GET request to /cheques/:id. However, when a cheque has progressed too far or has already been deleted, this will be the error message you receive.

{
    "error": {
        "type": "cancel_failed_error",
        "message": "Cannot cancel order ID cheque_7rjvmEM7yDz9TsoqAdiCJL."
    }
}

Resending Cheques Safely

In rare cases, cheques may not be returned when listing or you did not receive a response from your request to create a cheques. In this case, you may want to resend your cheques without worrying whether or not your previous requests were fulfilled. For such cases, PostGrid's API gives you the ability to make an idempotent request to create cheques by supplying a unique key in the header Idempotency-Key. Although you can designate this key however you would like, it is recommended that you use V4 UUIDs, more of which can be learned about here.

/**
 * @param {string} toContactID
 * @param {string} fromContactID
 * @param {string} bankAccountID
 * @param {string} cents
 * @param {string} idempotencyKey
 */
async function createChequeIdempotent(
    toContactID,
    fromContactID,
    bankAccountID,
    cents,
    idempotencyKey
) {
    const requestOptions = {
        method: 'POST',
        headers: {
            'x-api-key': API_KEY,
            'Content-Type': 'application/json',
            'Idempotency-Key': idempotencyKey,
        },
        body: JSON.stringify({
            to: toContactID,
            from: fromContactID,
            bankAccount: bankAccountID,
            amount: cents
        }),
    };

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

    return await resp.json();
}

With the above function, so long as the idempotencyKey remains the same, calling this function repeatedly will not create additional mail after it successfully reaches our servers. This will remain true even if you try to change the contacts or the template, and even would remain true if we tried to send a request with any other different option.