Actions
UpdatedHere’s a quick demonstration showing how actions work and how easy it is to change data structures to fit your needs.
How it works
Customer.io knows how to use the data you send in identify
calls and track
events. But your outbound integrations don’t all work the same way Customer.io does! So we have to reshape the data you send to Customer.io to fit the kinds of requests and data your outgoing integration expects.
That’s what an action does: it determines when we send data to your integration and how we map data in Customer.io to your integration.
- The when is what we call a trigger. You’ll see the trigger on your integration’s Actions page. This is the formula that tells us what incoming data result in a call to your integration.
- The how is the Data Structure. Your integration expects data in a specific shape, and the data structure lets you format your data to fit your outgoing integration.
In most cases, our default actions are all you’ll need
We’ve set up actions to support the majority of use cases and expected data structures. You’ll only want to change these if you aren’t using the out-of-the-box functionality with your integration.
a trigger} b-->|yes|c(Map data to
integration) b-.->|no|d(Do not forward
data to integration)


How many actions should I have?
The Type determines the kinds of things your integration can do. In general, you won’t have more actions than you have types!
By default, when you set up a new data-out integration, you’ll see an action for every available Type.
Action types
An action Type is the thing that you want to do in your outgoing integration.


For example, you’ll notice that the top type in our Customer.io destination is the Create or Update Person action. This is a fundamental action in Customer.io Journeys and maps nicely to an Identify call.
You can add/update and delete devices to, but these don’t map neatly to incoming data, so we look for events called Application Installed and Uninstalled!
Because different data-out integrations serve different purposes you’ll see different actions your different integrations!
Triggers and Filters
A Trigger determines when we send an action of the specified Type. It’s governed by conditions in the Filter field when you edit an action.
There are two typical filter fields:
type
represents the method or kind of incoming data:identify
,track
,page
,screen
,group
, andalias
.event
represents thename
field fromtrack
calls. Unlike other calls, track calls simply represent custom events, and the eventname
tells us what kind of event a person performed.
You can also filter based on other fields, but we’d recommend that you not get too granular with filters or you could inadvertently prevent data from reaching your destination!
Triggers on the Actions Tab | Filters within an Action |
---|---|
![]() ![]() | ![]() ![]() |
Data Structure
The Data Structure for each action determines the way we map incoming data to your outbound integration. Data structures use JSON notation for variables—but you don’t necessarily need to know JSON notation to change values. When you opt to change a mapping on the left, we’ll give you a drop-down of available Variables that you can select from incoming payloads.
In most cases, if you want to manipulate values, you’ll want to do it as a part of your data-in integrations—in your code. But we offer a couple of functions that can make things a little easier on you. Read more about the case and coalesce functions below.
Pick variables that match your incoming data
The list of available variables covers all possible incoming data. It isn’t limited to the type
or event
in your filter. If your action is based on an incoming identify
event, and you try to map a field to $.event
, that field will always be null
, because identify calls don’t include an event
!


Mapping to traits and event properties
While we know that incoming identify calls contain traits
and incoming track calls likely contain properties
, we don’t know what those properties are or might be. If you want to map to a specific trait or event property, you’ll need to provide it yourself—under traits.<trait-name>
or properties.<property-name>
respectively.
Make sure that your requests reliably provide the trait or event property in your incoming data or the property will be empty when we send data to your integration!
The case function
The case function is simple: it converts a value’s case to lower
or upper
. You might use this function to enforce uniform cases when you send data out of Customer.io. For example, you might want to store your users’ names in lowercase for uniformity.
The case function uses the format case(dataInKey, "lower/upper")
.
case(traits.first_name, "lower")
The coalesce function
The coalesce
function picks the first non-null value in a list of possible values. That’s a fancy way of saying that it uses the first non-empty value that it finds in the incoming payload.
The coalesce function uses the format coalesce(firstkey, secondkey)
. Only two arguments are supported at the moment.
For example, Customer.io lets you identify people by id
or email
address and we actually use coalesce($.userId, $.traits.email)
. So, if your identify
request has a userId
, we’ll map that to the person’s ID. If it doesn’t, we’ll map the email
trait to the person’s ID.
If you use coalesce
, and none of the keys are populated, the trait and the corresponding mapping will be null
(empty).
coalesce(userId, traits.email)
")-->b{Does the callhave a
userId
}
b-->|yes|c(Person ID = userId
)
b-.->|no|d{"Does the call havean
email
trait?"}
d-->|yes|e(Person ID = traits.email
)
d-.->|no|f(Person ID = null
)Hash function
The hash
function lets you hash a value using the SHA-256 algorithm. This is useful if you want to hash an email address or other personally identifiable information (PII) before sending it to an integration.
hash(traits.email)
Slugify function
The slugify
function converts a value to a slug—a value that consists only of letters, numbers, and hyphens. Slugifying a value removes all special characters, converts spaces to hyphens, and removes multiple hyphens. For example, slugify("Hello, World!")
returns hello-world
.
This might be useful when you want to convert a value to a URL-friendly identifier. For example, if you want to convert an email address to a slug that you use to identify a person in destination.
slugify(traits.email)
toJSON and fromJSON functions
These two functions help you handle stringified JSON data, but the naming convention can be a little confusing!
fromJSON
converts stringified JSON to a JSON object.toJSON
converts a JSON object to a string, similar toJSON.stringify
in JavaScript.
You may want to convert to or from stringified JSON if you’re working with a service that expects JSON data in a specific format.
var jsonTraits = {
"email": "cool.person@example.com",
"name": "Cool Person"
}
var stringifiedJSON = toJSON(jsonTraits)
// returns '{"email":"cool.person@example.com","name":"Cool Person"}'
fromJSON(stringifiedJSON)
// returns the original JSONTraits object
Empty values
You can map values to an integration, but that doesn’t guarantee that those values are populated in your incoming data.
Most incoming calls only require an ID of some sort or an event name, so you won’t necessarily receive errors if you send data into Customer.io that doesn’t fully populate all the data you want to capture in your outgoing integration. You’ll need to make sure sure that your data-in integrations send the right data—traits, event properties, etc—that you want to map to your outgoing integrations.