Getting started: key transactional concepts

Updated

How it works

Transactional messages are messages that your audience implicitly opts into—like purchase receipts, password reset requests, shipping updates, etc. Your audience expects to receive these messages even if they’ve opted out of marketing messages. That makes transactional differences different from marketing messages, which require explicit opt-in. And this difference isn’t just limited to Customer.io; it’s governed by laws like the CAN-SPAM Act and GDPR!

When someone does something that requires a transactional message, you’ll call our transactional API to send them a message—like when they request a password reset or need a receipt for a purchase. When you call the API, you can include data that you want to populate in your message, like a password reset URL or the purchase data.

sequenceDiagram actor a as your user participant b as your website participant c as Customer.io a->>b: request password reset b->>c: send transactional
API request c->>c: generate delivery c->>a: send password reset request

 Moving from another provider and need help? Try our Agency Partners page.

Go to our Agency Partners page, click Get Matched, and fill out the form; we’ll pair you with an agency that can help you create a transactional migration plan.

Use cases

Our transactional APIs help you separate transactional and marketing messages, while being able to see and manage both kinds of messages. We don’t charge extra for this service!

Common use cases for transactional messages include:

  • Purchase receipts
  • Registration confirmations (invites and opt-in confirmations)
  • Password resets
  • Trial expiration reminders
  • Comment notifications
  • Event reminders
  • Shipping updates
  • Support and feedback requests

Transactional emails have their own IP Pool

By default, we add email domains to Customer.io’s shared IP address pool to send emails. We manage and monitor multiple IP pools and remove domains that perform poorly to maintain high deliverability.

Under the Mail Servers tab of a domain, shared ip pool - default is selected.
Under the Mail Servers tab of a domain, shared ip pool - default is selected.

We also maintain a separate, transactional IP address pool. This pool has even higher standards—stricter bounce and spam thresholds—than our default, shared IP pool. This ensures that your transactional messages achieve the highest deliverability. Only domains used with the transactional service can send emails over this IP pool.

If you have a separate sending domain for your transactional messages, you can request that we add your domain to the transactional IP pool from your email settings. Select Manage Domain and click the Mail Servers tab.

Under the Mail Servers tab of a domain, shared ip pool - transactional is selected. You have to explain what you'll be sending and confirm your domain to request access.
Under the Mail Servers tab of a domain, shared ip pool - transactional is selected. You have to explain what you'll be sending and confirm your domain to request access.

 When we add a domain to the transactional pool, you can no longer use it to send campaigns or broadcasts.

You can also request dedicated IP addresses or set up a custom SMTP mail server from your email settings.

Identify your recipients

When you trigger a transactional message, you’ll specify an identifierThe attributes you use to add, modify, and target people. Each unique identifier value represents an individual person in your workspace. for a person—their id, email, or cio_idAn identifier for a person that is automatically generated by Customer.io and cannot be changed. This identifier provides a complete, unbroken record of a person across changes to their other identifiers (id, email, etc). (depending on the identifiers allowed by your workspace). If a person matching the id or email in your request doesn’t exist, we’ll create a new person.

This means that you can send transactional messages even when someone hasn’t signed up for your marketing messages.

Transactional message “templates”

When you create a transactional message in our UI, you’ll set a Trigger Name and we’ll generate a transactional_message_id. When you call the transactional API, you’ll use either of these values to tell Customer.io which transactional message you want to populate and send to your audience.

While you can omit these values and populate your own message body, subject, and from values at send time, we recommend that you create transactional message templates for every type of message you plan to send (“Receipt”, “Password Reset”, etc), even if you want to populate a custom message at send time, because it helps you effectively track metrics for your transactional messages.

Where do I find my transactional message ID?

You can find the transactional_message_id in a few places:

  • In the code sample in the Send Message step when you set up your message.
  • In the code sample in the Overview tab after you set up your message.
  • In the URL when you select a transactional message in the user interface. The number immediately following /transactional/ is the transactional_message_id.
  • You can return a list of your transactional messages, each containing the id (short for transactional_message_id), from the transactional API.

Transactional templates and uncategorized messages

To set up a transactional message you can either create a transactional template in Customer.io—a message that you’ll populate with data from an API call. Or you can send your entire message body through the API.

We recommend that you use templates for three reasons:

  1. It’s easier to design a message in Customer.io than send your entire HTML payload through the API.
  2. It reduces the size of the payloads you need to store in your integration.
  3. Most importantly, it makes it easy to track engagement with your transactional messages. We aggregate metrics by template ID. If you send your entire message through our API, you won’t have a a transactional_message_id, and we’ll track all your transactional messages as “uncategorized” messages! We aggregate metrics for uncategorized messages at the bottom of the transactional list, which can make it difficult for you to see how people respond to different types of transactional messages.
uncategorized_messages.png
uncategorized_messages.png

Trigger data and content variables

When using a transactional message template, you can personalize it like any other message using liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a person’s first name, you might use the variable {{customer.first_name}}.—like {{customer.first_name}} to use a person’s first_name attribute.

But you can also set values that you want to populate when you call our API using {{trigger.<message_data.property>}}. When you send a message using the transactional API, you can pass message_data to populate those variables.

Below is an example “password reset” message. First we have an example API call with a passwordResetUrl. That populates the {{trigger.passwordResetUrl}} liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a person’s first name, you might use the variable {{customer.first_name}}. in our example message.

message='
{
  "to": "sarah@example.io",
  "transactional_message_id": 3,
  "message_data": {
    "first_name": "Sarah",
    "passwordResetURL": "https://www.example.io/password?token=12345"
  },
  "identifiers": {  
    "id":"1234" 
  }
}
'

echo $message | curl -v https://api.customer.io/v1/send/email \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer APP-API-TOKEN' \
-d @-
password_reset.png
password_reset.png

Write fallbacks for liquid in transactional messages

Because transactional messages are important and time-sensitive, we’ll send messages to your customers even if they have liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a person’s first name, you might use the variable {{customer.first_name}}. errors. That means you’ll want to set fallbacks and make sure that your messages render properly even if a person doesn’t have the data you expect.

For example, imagine you write a message that uses people’s first names with Hi {{customer.first_name}}. If someone in your workspace doesn’t have a first name attribute, we won’t send a message to them.

To handle situations where people haven’t given you their first name, you can add a default value to the liquid statement to set a fallback value for people who don’t have a first_name attribute. For example, Hi {{customer.first_name | default: "Buddy"}} will send “Hi Buddy” if a person doesn’t have a first name.

When you draft your message, we also won’t know who your transactional audience is. This means that we can’t validate the attributes and other properties you reference in liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a person’s first name, you might use the variable {{customer.first_name}}.. This is another reason to set fallbacks in your liquid statements.

 Did you open your account before Nov 28, 2023?

If you opened your Customer.io account before Nov 28, 2023, you may be using our legacy version of liquid. For our legacy versions of liquid, fallback statements are a little different. Learn more and check which version of liquid you’re on.

Scheduling transactional messages

You can use the send_at parameter in your transactional API request to schedule your message for up to 90 days in the future. This helps if customers sign up for time-sensitive transactional messages, so you don’t have to manually schedule your transactional API requests in your backend system.

You might use send_at to send booking reminders as a person’s vacation rental or event approaches. Or you might let customers who expressed interest in a product before it’s released know exactly when it becomes available.

By default, we retain the content of the messages you send and track links in transactional messages. But you might not want to do this for transactional emails with sensitive information—personal customer information, password reset tokens, etc.

For sensitive messages, you can enable the Protect sensitive data setting. This setting prevents Customer.io from storing the body of transactional messages. When you enable the Protect sensitive data setting, you also necessarily disable link tracking in your message because we don’t retain the message.

with protect sensitive data enabled, we can't track messages
with protect sensitive data enabled, we can't track messages

Localize transactional messages

When you add content to your message, you can click Add language to select the languages you want to support. Then you can populate your languages, and we’ll automatically send your audience the language matching their language attributeA key-value pair that you associate with a person or an object—like a person’s name, the date they were created in your workspace, or a company’s billing date etc. Use attributes to target people and personalize messages. Attributes are analogous to traits in Data Pipelines..

 You can override languages when you send your message

If you don’t manage your audience’s language preferences in Customer.io, or you need to override your audience’s language preference for any reason, you can set a language property in your transactional message request. Learn more

Add languages to your newsletter
Add languages to your newsletter

To take advantage of our localization feature, you must have set up an attribute to capture your audience’s language preference. See our localization section for more information about setting up multi-language messages.

When you send your message, we’ll match your audience’s language attributeA key-value pair that you associate with a person or an object—like a person’s name, the date they were created in your workspace, or a company’s billing date etc. Use attributes to target people and personalize messages. Attributes are analogous to traits in Data Pipelines.—their preferred language—with the languages in your transactional message. If a person’s language attribute matches a language in your transactional message, we’ll send them the appropriate language. If their language attribute does not match a language in your template, they’ll receive the default message.

graph LR A[Person completes
a transaction] --> B[Send transactional
api call] B --> D{Does a person's language
attribute match a message?} D -->|no| H[Person gets
default message] D -->|yes, lang=es| E[Person gets
Spanish message] D -->|yes, lang=fr| F[Person gets
French message] D -->|yes, lang=de| G[Person gets
German message]

Overriding your audience’s language

If you don’t manage your audience’s language preferences in Customer.io, or you need to override your audience’s language preference for any reason, you can set a language property in your transactional message request. This value represents the language variant that you want to send to the recipient.

If the language doesn’t match one of your message’s languages, we’ll use the recipient’s language attributeA key-value pair that you associate with a person or an object—like a person’s name, the date they were created in your workspace, or a company’s billing date etc. Use attributes to target people and personalize messages. Attributes are analogous to traits in Data Pipelines.. If the language doesn’t match one of your message’s languages, and your audience doesn’t have a language attribute that matches one of your message’s languages, we’ll send the default message.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{

  "transactional_message_id": 44,
  "to": "cool.person@example.com",
  "from": "override-templated-address@example.com",
  "subject": "Did you really login from a new location?",
  "language": "es",
  "identifiers": {
    "id": 12345
  },
  "message_data": {
    "password_reset_token": "abcde-12345-fghij-d888",
    "account_id": "123dj"
  },
  "bcc": "bcc@example.com",
  "disable_message_retention": false,
  "send_to_unsubscribed": true,
  "tracked": true,
  "queue_draft": false,
  "disable_css_preprocessing": true
}
Copied to clipboard!
  Contents
Is this page helpful?