Skip to main content

Introduction

Automations is a fully managed orchestration service that makes it easy to design complex notification workflows with one API. Most notification workflows are highly nuanced and require specific delivery logic to delight the end user. Some simple examples include delaying the delivery of a notification, fetching the latest data to include with a notification, canceling the delivery of a notification, or scheduling a reoccurring notification. These types of delivery strategies can easily be implemented using Courier Automations.

The Courier Automation Toolset

There are two ways to utilize the power of Courier Automations, simply using the /automation/invoke API or by using the visual template designer.

The Invoke API

It is possible to quickly create and invoke an Automation Run using only the /automation/invoke API. With this approach the Automation definition is defined in the request body and is immediately executed at request time.

For example, lets say we want to send a "Tips and Tricks" message to a new user who signs up to our application. We don't want to overwhelm the new user with notifications right after they sign up, so we will delay the message delivery by 1 day. In this case we can simply define the Automation Run as JSON:

{
"automation": {
"steps": [
{
"action": "delay",
"duration": "1 day"
},
{
"action": "send",
"recipient": "user_id",
"template": "TIPS_AND_TRICKS"
}
]
}
}

and we could use an http client to make the following API call:

const axios = require("axios");

const data = JSON.stringify({
automation: {
steps: [
{
action: "delay",
duration: "1 day",
},
{
action: "send",
recipient: "user_id",
template: "TIPS_AND_TRICKS",
},
],
},
});

axios({
method: "post",
url: "https://api.courier.com/automations/invoke",
headers: {
Authorization: "Bearer token",
"Content-Type": "application/json",
},
data,
})
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});

For such simple use cases it is often convenient to define the Automation workflow directly in the API request body. However if your use case requires more logical steps, scheduling functionality or if you would just like Courier to save the Automation definition for future use, then consider using the The Template Designer.

For more information on additional properties see the Passing Dynamic Data section below or visit the Automation Steps documentation section.

The Template Designer

The Automation Template Designer is an ideal tool to define a series of Automation Steps once and invoke them with either an Automation Trigger or an API call.

Using the above example, imagine we no longer want to manage the Automation code, but instead we want Courier to store that logic. This allows us to easily edit the Automation Run in the future while reducing our Courier code footprint.

We can turn the above call into a Automation Template, using the following procedure:

  1. Navigate to https://app.courier.com/designer/automations and click Create Template.
  1. Name the template and alias DELAYED_DELIVERY and toggle the editor to "Code Mode"
  1. Copy and paste the request body from the original request into the Automation editor and click Publish:
{
"steps": [
{
"action": "delay",
"duration": "1 day"
},
{
"action": "send",
"recipient": "user_id",
"template": "TIPS_AND_TRICKS"
}
]
}
  1. Now we can invoke the Automation Template using the alias we provided in step 2.
const axios = require("axios");

axios({
method: "post",
url: "https://api.courier.com/automations/DELAYED_DELIVERY/invoke",
headers: {
Authorization: "Bearer token",
"Content-Type": "application/json",
},
})
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});

As you can see this application code is now a lot less to read and maintain. We also get the added benefit of be able to update the Automation Template, without updating our application code. Lets extend this example to wait an additional day and then fetch user usage data and send it to our new user. This is quickly implemented leveraging the Automation Template designer.

  1. Toggle the editor back to "Visual Mode".
  1. Select "New Step", and then select "Delay"
  1. Edit the Delay form to wait for 1 Day.
  1. Select "New Step" again, and then select "Fetch Data". Then update the Webhook textarea in Fetch Data form to the following:
{
"url": "https://myapp.com/users/usage",
"method": "GET",
"params": {
"userId": {
"$ref": "data.user_id"
}
}
}

We will discuss the syntax "$ref": "data.user_id" in the Accessing Dynamic Data section.

  1. Select "New Step" again, and then select "Send", and update the Send form.
  1. Publish the template.

If you want to invoke the Automation on a schedule or in response to an event see the Triggers section.

That's it! Now whenever a new user signs up to our application, we will execute the following workflow:

  1. Wait 1 Day
  2. Send a TIPS_AND_TRICKS message to incentive user activity.
  3. Wait 1 Day
  4. Fetch the latest usage report for our new user.
  5. Send a USER_USAGE_REPORT message to incentive user engagement.

Accessing Dynamic Data

As we have seen it is a common use case to send dynamic data to your Automation Run. After all using the latest data to send a notification is often the most engaging.

There are a few ways to access dynamic data in an Automation Run.

The Automation Run Cache

The Automation Run Cache is essentially a JSON object that an Automation Step from the Automation Run writes values to. This allows Automation Steps to share data across the entire Automation Run. In other words, one Automation Step can update the cache, while another Automation Step, that gets executed later, can read from it. The cache is only valid for the length of the run, so only steps that belong to the Automation Run can access the Automation Cache.

Initializing the Automation Run Cache

To initialize the Automation Cache, you can invoke the Automation Run with any of the following properties:

  • brand
    • type: string
    • description: A unique identifier that represents the brand that should be used for rendering the notification.
  • data
    • type: object
    • description: An object that includes any data you want to pass to a message template or accessor type. The data will populate the corresponding template variables.
  • profile
    • type: object
    • description: an object that includes any key-value pairs required by your chosen Integrations (see our Provider Documentation for the requirements for each Integration.) If profile information is included in the request and that information already exists in the profile for the recipientId, that information will be merged.
  • template
    • type: string
    • description: A unique identifier that can be mapped to an individual Notification. This could be the "Notification ID” on Notification detail pages (see the Notifications page in the Courier app) or a custom string mapped to the event in settings.
  • recipient
    • type: string
    • description: A unique identifier associated with the recipient of the delivered message, which can optionally map to a Courier managed profile. -->

Continuing with our example, lets initialize the Automation Cache with the user_id of our new user. We will add the user_id to the data property of the Automation Run Cache, and include it in our https request body:

const axios = require("axios");

const user = db.createUser();

axios({
method: "post",
url: "https://api.courier.com/automations/DELAYED_DELIVERY/invoke",
headers: {
Authorization: "Bearer token",
"Content-Type": "application/json",
},
data: {
data: {
user_id: user.id,
},
},
})
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});

Reading from the Automation Run Cache

To read values from the Automation Cache you can use an "accessor type":

{
"action": "send",
"recipient": {
"$ref": "data.user_id"
}
}

An "accessor type" is simply an object with the key $ref and a string value that represents a property path to the Automation Cache object.

Lets update the Send step to read from Automation Cache.

  1. First switch the designer to "code mode", and update the recipient property value to:
"recipient": {
"$ref": "data.user_id"
}

and click Publish

Now if you switch the designer back to "Visual Mode", you will see the Send Form has also updated.

The Recipient ID field of the Send Form has changed to refs.data.user_id. This is a "form accessor type", and is analogous to the "accessor type" syntax described above, but is only valid in the Automation Step form editor.

Writing to the Automation Run Cache

There are two Automation Steps used to write data to the Automation Cache, Fetch Data and Send List.

We have already seen the Fetch Data Step earlier in this document. As a refresher, we simply provide a webhook url to the Automation Step.

{
"action": "fetch-data",
"webhook": {
"url": "https://myapp.com/users/usage",
"method": "GET",
"params": {
"userId": {
"$ref": "data.user_id"
}
}
},
"merge_strategy": "replace"
}

When this step is executed, it will make an http request to "https://myapp.com/users/usage" and replace the Automation Cache's data property with the response.

If you would like instead to merge the http response with the existing data in the Automation Cache you can provide a different merge_strategy:

  • replace
    • overwrite all properties in the Automation Cache with the http response. Remove's all in properties in the Automation Cache that do not exist in http response.
  • soft-merge
    • only overwrite properties in the Automation Cache with the http response properties that do not yet exist in the Automation Cache.
  • overwrite
    • overwrite all properties in the Automation Cache with the properties from the http response.
  • none
    • do not make any changes to the Automation Cache if the Automation Cache already exists and has data. Otherwise initialize the Automation Cache.

The second step used to write data to the Automation Cache is the Send List step. For every recipient in the courier list, a the webhook property to the optional data_source object. if called via http, and the run context is updated with the response.

{
"action": "send-list",
"template": "USER_USAGE_REPORT",
"list": "MY_COURIER_LIST",
"data_source": {
"webhook": {
"url": "https://myapp.com/users/usage",
"method": "GET",
"headers": {}
},
"merge_strategy": "replace"
}
}

When a Send List Automation Step is configured with the optional data_source property, the webhook url will be called with the recipient_id as a query param for each user in the list. It will then update the Automation Cache with the response and passed to the Notification Template and sent as a notification message.

For more information see Send List in the Steps section.

Canceling the Automation Run

Finally we cover how to cancel an Automation Run that is in progress. Lets extend our example again and imagine we want to cancel the DELAYED_DELIVERY Automation Run if our new user deletes their account in our application. If this event occurs at any point during the Automation Run's execution, then we want to cancel the Automation Run and not send any more messages.

  1. First we need to make our Automation Run "cancelable" by providing a Cancelation Token value for our Automation Run. Lets name our token the same value as our users user_id by using the "accessor form type" refs.data.user_id
  1. Next, create Automation by selecting All Automations, then clicking Create Template. Give the Automation Template a name and alias of CANCEL_NEW_USER_WORKFLOW. Click Publish.
  1. Click New Step and select a Cancel Automation from the menu. Set the value using the same "accessor form type" refs.data.user_id.
  1. Now lets update our application code to invoke the CANCEL_NEW_USER_WORKFLOW Automation when the user removes their account.
const axios = require("axios");

const user = db.deleteUser(id);

axios({
method: "post",
url: "https://api.courier.com/automations/CANCEL_NEW_USER_WORKFLOW/invoke",
headers: {
Authorization: "Bearer token",
"Content-Type": "application/json",
},
data: {
data: {
user_id: user.id,
},
},
})
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});