Introduction to Courier Automations

  • Courier Automations provide a toolset to design and implement complex notification workflows which allow you to send automation templates through an API, or a programmatic ad-hoc automation.
  • An Automation is a schema definition that enables developers to easily express complex use cases such as digests, delayed messages, scheduled messages, sending messages to an ad hoc list, and escalation messages.
  • At a high level, an Automation schema is composed of two parts, Context Properties and Steps like send, delay, update-profile, fetch-data where each step represents a specific action that the automation will execute on your behalf when an event is sent to the Automation API.

Sample Ad-Hoc Automation Schema:

// URL: https://api.courier.com/automations/invoke
// Method: POST
{
   "automation":{
      "steps":[
         {
            "action": "send",
            "template": "SAMPLE_TEMPLATE"
         },
         {
            "action": "delay",
            "until": "20240408T080910.123"
         }
      ]
   },
   "data":{
      "name": "Spike Spiegel",
      "age": 27
   },
   "profile":{
      "user_id": "spiegel",
      "email": "spike@bebop.com"
   }
}

Automation Run Context

  • The run context refers to a set of special properties, that are used to invoke an automation. Context properties are available to all steps at Step Execution Time.
  • Run Context can be defined as part of an ad-hoc automation or passed as a first-class object to an automation template.

Run Context Properties:

PropertyTypeDescription
brandstringA unique identifier that represents the brand should be used to render the notification.
dataobjectAn object that includes any data you want to pass to a message template or accessor type. The data will populate the corresponding template variables.
profileobjectAn 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 user_id, that information will be merged.
templatestringA unique identifier that can be mapped to an individual notification. This can be the notification_id.
recipientstringA unique identifier. Associated with the recipient of the delivered message, which can optionally map to a Courier user profile.

Structuring With Automation Steps

Steps are the fundamental building blocks of an automation schema. Automation steps are a set of actions the automation will execute on your behalf, which are used together to implement complex messaging workflows.

There are seven Automation steps that you can use to build your workflow:

ActionDescription
sendA send step will deliver a single notification to a recipient. The recipient and template properties are required. Template refers to the event or notification_id of the notification to be sent
send-listSend a message to each recipient in the list. Accepts a list_id value.
fetch-dataFetch data via HTTPS and write the response to the data property of the Automation run context.
delayWait a duration of time before proceeding to the next automation step. Accepts duration and until values.
update-profileUpdate the Courier profile of a given user_id.
invokeInvoke another automation template. Accepts template as a unique identifier that can be mapped to another automation template.
cancelCancel an automation run that is In progress. Accepts a string that is associated with the cancelable automation run as cancelation_token.

Send Schema

For send steps, the recipient and template properties are required. recipient refers to any nonempty recipient_id that you can define to keep track of and identify the notification’s recipient across messages.

{
  "action": "send",
  "data": {}, // optional
  "if": "<CONDITIONAL_EXPRESSION>", // optional javascript boolean expression
  "override": {}, // optional: send provider override
  "profile": {}, // optional
  "recipient": "<RECIPIENT_ID>",
  "ref": "<REFERENCE>", // optional
  "template": "<TEMPLATE_ID>",
  "idempotency_expiry": "<IDEMPOTENT_EXPIRATION>", // optional
  "idempotency_key": "<IDEMPOTENT_KEY>" // optional
}

Send-List Schema

For send-list actions, the list and template properties are required.

{
  "action": "send-list",
  "brand": "<BRAND_ID>", // optional
  "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
  "ref": "<REFERENCE>", // optional
  "override": {}, // optional: send provider override
  "data": {}, // optional
  "list": "<LIST_ID>",
  "template": "<TEMPLATE_NAME_OR_ID>",
  "data_source": { // optional
    "webhook": {
      "body": {}, // optional
      "headers": {}, // optional
      "params": {}, // optional
      "method": "GET" | "POST", // optional, default GET
      "url": "<API_RESOURCE>"
    },
    "merge_strategy": "replace|overwrite|soft-merge|none"
   },
  "idempotency_expiry": "<IDEMPOTENT_EXPIRATION>", // optional
  "idempotency_key": "<IDEMPOTENT_KEY>" // optional
}
  • You can use the data-source property to define an API resource to be used to render a notification template. If using data-source, a merge strategy and a webhook with a url must be given.
  • The data_source.webhooks.url should accept a recipientId query string parameter.
  • The response from the data_source property will be merged with the existing automation data that was defined at runtime, based on the merge strategy, and will be passed to the notification template as the data property. See more on the different merge strategies under the update-profile section.

Example: Idempotent send-list step

{
  "action": "send-list",
  "webhook": {
    "body": {}, // optional
    "headers": {}, // optional
    "params": {}, // optional
    "method": "GET" | "POST", // optional, default GET
    "url": "<API_RESOURCE>" //HTTPS protocol required
  },
  "merge_strategy": "replace|overwrite|soft-merge|none",
  "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
  "ref": "<REFERENCE>", // optional
  "idempotency_expiry": "<IDEMPOTENT_EXPIRATION>", // optional
  "idempotency_key": "<IDEMPOTENT_KEY>" // optional
}

SEND IDEMPOTENCY FEATURES a send and send-list steps support idempotency for safely retrying requests without accidentally performing the same operation twice.

To define an idempotent send or send-list step, provide an idempotency_key property on the step. An idempotency key is a unique value generated by the client which the server uses to recognize subsequent retries of the same request. How you create unique keys is up to you, but we suggest using V4 UUIDs, or another random string with enough entropy to avoid collisions.

In addition to the idempotency_key , you can define an idempotency_expiry that allows you to set an expiration on the idempotency_key that is longer than 24 hours (up to 1 year).

See Idempotent Requests for more on Idempotency.

Fetch-Data Schema

A fetch-data step will fetch data from an API resource and store it into run context to be used for subsequent step executions. webhook and merge_strategy are required.

{
  "action": "fetch-data",
  "webhook": {
    "body": {}, // optional
    "headers": {}, // optional
    "params": {}, // optional
    "method": "GET" | "POST", // optional, default GET
    "url": "<API_RESOURCE>"
  },
  "merge_strategy": "replace|overwrite|soft-merge|none",
  "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
  "ref": "<REFERENCE>", // optional
  "idempotency_expiry": "<IDEMPOTENT_EXPIRATION>", // optional
  "idempotency_key": "<IDEMPOTENT_KEY>" // optional
}

The response from the fetch-data step will be merged with the existing automation data that was defined at runtime, based on the merge strategy, and will be passed to the notification template as the data property.

Delay Schema

There are two ways to delay an automation step

  • Delay until a specific time
  • Delay for a duration of time

When using the delay step, an automation will be processed serially. Each item in the automation is processed one at a time and the next step will not be undertaken until the current one has completed.

There is a 5-15 minute variance in when delay step actions will finish processing.

Delay Until a Specific Time

To delay a step until a specific time, set the time using an ISO-8601 timestamp.

{
  "action": "delay",
  "until": "2021-02-18T18:40:05.960Z", // ISO-8601timestamp
  "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
  "ref": "<REFERENCE>" // optional
}

Delay For a Period of Time

You are able to delay an automation step for the following whole increments: -

  • minute(s)
  • hours(s)
  • day(s)
  • month(s)
{
  "action": "delay",
  "duration": "1 hour",
  "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
  "ref": "<REFERENCE>" // optional
}

Update-Profile Schema

An update-profile step will update the recipient’s profile according to the merge strategy provided in the step. recipient_id, profile, and merge are required.

{
  "action": "update-profile",
  "recipient_id": "<RECIPIENT_ID>",
  "profile": {},
  "merge": "replace|overwrite|soft-merge|none",
  "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
  "ref": "<REFERENCE>" // optional
}

Invoke Schema

The invoke step allows you to execute another automation template. The template property is required, and the optional context object defines the data and properties to invoke the new, targeted template with.

{
  "action": "invoke",
  "template": "<TEMPLATE_ID>",
  "context": {
    //optional
    "brand": "<BRAND_ID>", // optional
    "data": {}, // optional
    "profile": {}, // optional
    "template": "<NOTIFICATION_TEMPLATE>", // optional
    "recipient": "<RECIPIENT_ID>" // optional
  },
  "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
  "ref": "<REFERENCE>" // optional
}

Cancel Schema

Automations can be marked as cancelable by providing a cancelationToken then canceled by providing a cancellation token on a subsequent automation. All automations with a matching cancellation token will be canceled.

{
  "cancelationToken": "<CANCELATION_TOKEN>",
  "steps": [
    // ... step configuration
  ]
}

If you want to cancel automation on a per-recipient basis there are two options:

  1. Use Ad Hoc automation and provide a unique cancelationToken per recipient
  2. OR Use an automation template and set a dynamic cancellation token each time you invoke the automation like this:
{
  "cancelation_token": {
    "$ref": "data.foo.token"
  },
  "steps": [
    {
      "action": "",
      "template": "",
      "recipient": "",
      "profile": {
        "email": ""
      }
    }
  ]
}
Cancelling automations using a cancelationToken
{
  "steps": [
    {
      "action": "cancel",
      "cancelationToken": "<CANCELATION_TOKEN>",
      "if": "<CONDITIONAL_EXPRESSION>", // optional boolean expression
      "ref": "<REFERENCE>" // optional
    }
  ]
}

Step Conditionals

  • For each type of step, you can define conditions that will determine if a step is executed or not. - Adding an if property to a step makes it a conditional step.
  • The value of the if property should be a valid javascript expression that evaluates to a boolean.
  • Properties set in the run context are available for use in these expressions.

Example: The following send step has a conditional expression that should evaluate to true. Therefore, the step will be executed.

{
  "template": "TEST",
  "recipient": "abcd-1234-efgh-5678",
  "profile": {
    "email": "test@gmail.com"
  },
  "data": {
    "foo": "send-it"
  },
  "automation": {
    "steps": [
      {
        "action": "send",
        "if": "data.foo === 'send-it'"
      }
    ]
  }
}

Step References

  • You can define a step-level reference, by adding a ref property to a step definition.
  • In the conditionals of subsequent steps, you can then reference metadata about the sent message using the word refs.

Example If you only want to send a follow-up message if that message hasn’t been opened yet, you can define the following automation run:

{
  "automation": {
    "steps": [
      {
        "action": "send",
        "template": "OUTREACH",
        "ref": "outreach"
      },
      {
        "action": "delay",
        "delayFor": "24 hours"
      },
      {
        "action": "send",
        "template": "FOLLOWUP",
        "ref": "followup",
        "if": "refs.outreach.status < MessageStatus.Opened"
      }
    ]
  }
}

In the above example, refs.outreach allows us to access information in the step context of the first send step. For send steps, this step context includes information about the status of the send notification.

Merge Strategy

Some automation steps require or include a merge strategy.

In the following merge strategy definitions, A = data defined in the step; B = existing data.

StrategyDescription
replaceOverwrites all properties in B from A; remove properties in B that do not exist in A
overwriteOverwrite all properties in B from A
soft-mergeOnly overwrites properties in B from A that do not yet exist in B
noneNo changes to B if B already exists; else B = A

Run-Step Composition

There are a few object properties that can be defined at both the step and the run level. Run level data and profile properties will compose into each step, with already provided step level properties taking precedence.

For example, the “Title” property will compose into and be available for each step.

{
  [...]
  "data": {
    "Title": "Back to the Future"
  }
  "automation": {
    "steps": [
      {
        [...]
        "data": {
          "Username": "Doc Brown"
        }
      },
      {
        [...]
        "data": {
          "Username": "Marty McFly"
        }
      },
      [...]
    ]
  }
}