Customer.io
Introduction
The Customer.io App for Flows enables you to connect your workflows to Customer.io, a messaging and customer engagement platform. This integration allows you to create and update people, track behavioural events, trigger journeys, send transactional email and push, and read your campaigns, broadcasts and segments, directly from your Flows without writing complex code. With this App, you can build automated workflows that keep Customer.io in sync with player and customer activity in real time, perfect for registration onboarding, behavioural campaign triggers, and transactional messaging.
What is Customer.io?
Customer.io is a customer engagement platform that stores a profile for every person, records the events they generate, and uses both to drive automated messaging journeys across email, push, SMS and in-app. Key features include:
- People profiles with unlimited standard and custom attributes
- Event tracking that triggers and branches campaigns
- Campaigns, API-triggered broadcasts and segments
- Transactional email and push with reusable templates
- Suppression and device management for compliance and mobile messaging
Getting Started
Prerequisites
Before using the Customer.io App, you will need:
- A Customer.io account on the EU data centre
- Your Track API Site ID and Track API Key
- An App API Key
Finding Your Credentials
- Log in to Customer.io and go to Settings > Account Settings > API Credentials
- Open the Track API Keys tab and copy the Site ID and the API Key — these are your
siteIdandtrackApiKey - Open the App API Keys tab, click Create App API Key, name it, and copy the generated token — this is your
appApiKey
Recommendation: the Site ID and Track API Key travel as HTTP Basic authentication, and the App API Key as a Bearer token. Keep all three secure — anyone holding them can read, message and delete every person in your workspace. The App API Key is shown only once when created, so store it before closing the dialog.
Region: this App is hardcoded to the Customer.io EU data centre. Credentials from a US workspace will return 401 on every stage.
Available Stages
| Stage | Method | Description |
|---|---|---|
| Create or Update Person | PUT | Create a person or update one, sending any attributes as a JSON object |
| Identify Person | PUT | Legacy — create or update a person with an email address only |
| Track Event | POST | Record a named event against a known person |
| Track Anonymous Event | POST | Record an event with no person attached |
| Delete Person | DELETE | Permanently delete a person and their history |
| Suppress Person | POST | Stop all messaging to a person without deleting them |
| Unsuppress Person | POST | Resume messaging to a suppressed person |
| Add or Update Device | PUT | Register a mobile push token against a person |
| Delete Device | DELETE | Remove a push token from a person |
| Add to Segment | POST | Add people to a manual segment |
| Remove from Segment | POST | Remove people from a manual segment |
| Trigger Broadcast | POST | Start an API-triggered broadcast |
| Send Transactional Email | POST | Send a one-off email from a transactional template |
| Send Transactional Push | POST | Send a one-off push from a transactional template |
| Look Up Person by Email | GET | Find people by email address |
| Get Person Attributes | GET | Read every attribute stored on a person |
| List Campaigns | GET | Return every campaign |
| List Broadcasts | GET | Return every broadcast |
| List Segments | GET | Return every segment |
| List Transactional Messages | GET | Return every transactional template |
Authentication
Customer.io splits its functionality across two APIs with two different credentials. The App handles this for you — the connection collects all three values once, and each stage uses the right pair. There is no OAuth, no token refresh and no expiry handling.
Track API — used by every stage that pushes data in: Create or Update Person, Identify Person, Track Event, Track Anonymous Event, Delete Person, Suppress and Unsuppress Person, Add or Update Device, Delete Device, Add to and Remove from Segment.
Authorization: Basic base64(siteId:trackApiKey)
Base URL: https://track-eu.customer.io/api/v1
App API — used by every stage that sends a message or reads data back: Trigger Broadcast, Send Transactional Email, Send Transactional Push, Look Up Person by Email, Get Person Attributes, and all four List stages.
Authorization: Bearer <appApiKey>
Base URL: https://api-eu.customer.io/v1
Create or Update Person
The Create or Update Person stage creates a person in Customer.io or updates an existing one, sending any attributes you want as a single JSON object. This is the stage to use on a registration event.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| ID | {{id}} | Yes | The unique identifier of the person in Customer.io — normally your own user ID from the registration event. If no person has this ID one is created, if one exists it is updated. Use one identifier type consistently across your workspace or you will create duplicate people. |
| Payload | {{payload}} | Yes | JSON object with the attributes to set on the person. Keys are the Customer.io attribute names as they appear in your workspace; values can be fixed text or placeholders from earlier stages in your flow. Custom attributes work like standard ones — Customer.io creates them on first use. Attributes you do not send keep their current value. Always include email or the person exists but cannot be messaged. |
Output
| Field | Description |
|---|---|
| success | true when Customer.io accepted the upsert |
| id | The person ID you sent — pass it to Track Event, Suppress Person or Add or Update Device |
Usage Example
| Parameter | Value |
|---|---|
| id | {{event.userId}} |
| payload | {"email":"{{event.email}}","first_name":"{{event.firstName}}","country":"{{event.country}}","signup_date":"{{event.createdAt}}"} |
Important Notes
- The Payload field is your mapping layer. The left side of each pair is the attribute name in Customer.io, the right side is the data coming from your flow. When a different client names the same attribute differently, you edit this one field in their flow — nothing else changes
- Attributes are merged, not replaced. Sending
{"vip_level":"gold"}later updates only that attribute and leaves the rest intact, so the same stage covers registration and every later profile update - The Payload must be a valid JSON object. A trailing comma or an unquoted key returns a 400
- This stage supersedes Identify Person, which can only send an email address
- If your workspace identifies people by both id and email, sending a new id with an email that already belongs to someone does not create a duplicate — Customer.io updates the existing person and assigns them the new id instead. This is a common source of confusion when testing: reusing the same email with different generated IDs updates one person repeatedly, it does not create multiple people
- You can also set
unsubscribed:truedirectly inside the Payload to suppress a person in the same call that updates their other attributes — this has the same effect as running Suppress Person separately, and can save a stage if you're already sending a Payload
Identify Person
The Identify Person stage creates or updates a person with an email address only. It is kept for flows already using it — new flows should use Create or Update Person.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| ID | {{id}} | Yes | The unique identifier of the person in Customer.io |
{{email}} | Yes | The person's email address — without it Customer.io cannot message them |
Output
Returns success, id and email.
Important Notes
- This stage calls exactly the same Customer.io endpoint as Create or Update Person — the only difference is that it can send nothing but the email address
Track Event / Track Anonymous Event
These stages record an event in Customer.io. Track Event attaches it to a known person; Track Anonymous Event records it with no person attached, for activity from visitors you have not identified yet.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| ID | {{id}} | Yes (Track Event only) | The person the event belongs to. The person does not have to exist first — Customer.io creates a profile for an unknown ID |
| Event Name | {{eventName}} | Yes | Name of the event, e.g. purchase, signup, deposit. This is the string you select when building a campaign trigger in Customer.io, so keep it stable — renaming it breaks existing campaigns. Customer.io creates the event type on first use |
Output
| Field | Description |
|---|---|
| success | true when the event was accepted |
| id / eventName | The values you sent, echoed back |
Usage Example
| Parameter | Value |
|---|---|
| id | {{event.userId}} |
| eventName | first_deposit |
Important Notes
- Events are accepted asynchronously. A success response means Customer.io queued the event, not that a campaign has fired — allow a few seconds before checking the person's Activity log
- Anonymous events can only trigger campaigns that do not target a person, so use Track Event whenever you have an ID
Delete Person / Suppress Person / Unsuppress Person
These three stages act on an existing person using their ID. Suppress stops all messaging while keeping the record; Delete removes the person entirely.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| ID | {{id}} | Yes | The person's identifier in Customer.io |
Output
All three return success and the id you sent.
Important Notes
- Delete Person is permanent and takes the person's event history with it — there is no undo
- Suppress Person is the right choice for an unsubscribe or a GDPR objection: the person stops receiving messages but their history survives for reporting
- A suppressed person is silently skipped by every campaign. If messages stop arriving for one user and nothing looks wrong, check whether they are suppressed
- Deleting a person who does not exist still returns success — these calls are idempotent
Add or Update Device / Delete Device
These stages register or remove a mobile push token on a person, which is what makes push messaging possible for that user.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| ID | {{id}} | Yes | The person who owns the device |
| Device Token | {{deviceToken}} | Yes | The push token issued by APNs or FCM. It comes from your own mobile app — it is not visible anywhere in the Customer.io UI |
| Platform | {{platform}} | Yes (Add or Update Device only) | Must be exactly ios or android, lowercase. Any other value is rejected |
Output
Add or Update Device returns success, id and deviceToken. Delete Device returns success and id.
Important Notes
- Push tokens rotate. Call Add or Update Device on every app launch rather than only at registration, or your push delivery will silently decay over time
- Call Delete Device on logout, otherwise the next person to use that handset receives the previous user's notifications
Add to Segment / Remove from Segment
These stages add or remove people from a manual segment in Customer.io.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| Segment ID | {{segmentId}} | Yes | Numeric ID of a manual segment. Run List Segments to get it, or read it from the segment's URL in Customer.io |
| Customer IDs | {{customerIds}} | Yes | Comma-separated list of person IDs, e.g. user_1,user_2,user_3. Use the same identifier type as the ID parameter elsewhere |
Output
Both return success and the segmentId you sent.
Important Notes
- Only manual segments can be edited through the API. A data-driven segment computes its own membership and the call will fail
- Person IDs that do not exist are ignored silently rather than returning an error, so a success response does not prove every ID landed
Trigger Broadcast
The Trigger Broadcast stage starts an API-triggered broadcast in Customer.io — the way to launch a Customer.io journey from a Flow.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| Broadcast ID | {{broadcastId}} | Yes | Numeric ID of the broadcast to start. Run List Broadcasts to find it |
Output
| Field | Description |
|---|---|
| id | Identifier of the triggered run, for correlating with Customer.io's delivery reporting |
| broadcastId | The broadcast ID you sent, echoed back |
Important Notes
- The broadcast must be created in Customer.io as an API-triggered broadcast. Triggering any other type returns 404, which reads like a wrong ID but is not
- The broadcast must also be live in Customer.io — a draft returns 404 as well
Send Transactional Email / Send Transactional Push
These stages send a one-off message rendered from a transactional template — receipts, password resets, shipping confirmations. They bypass campaigns entirely.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
| Transactional Message ID | {{transactionalMessageId}} | Yes | ID of a transactional template configured in Customer.io under Messages > Transactional. Run List Transactional Messages to get it |
| To | {{to}} | Yes (email only) | Recipient email address |
| Identifier ID | {{identifierId}} | Yes | The person ID the message is attributed to in Customer.io — normally the same value as the ID used elsewhere. Required even when To is set, because Customer.io logs the delivery against the person |
Output
| Field | Description |
|---|---|
| delivery_id | Customer.io's identifier for this send — use it to find the message in Deliveries |
| queued_at | Timestamp the message entered the sending queue |
A delivery_id means queued, not delivered. Sending happens asynchronously — check Deliveries in Customer.io, or use a Customer.io webhook, if you need delivery confirmation.
Important Notes
- The template must be live. A draft template returns 400
- Send Transactional Push reaches only people who have a device registered through Add or Update Device. With no device on the profile the call succeeds and nothing arrives
- A suppressed person receives nothing, transactional messages included
Look Up Person by Email / Get Person Attributes
These read stages find a person or read back everything stored on one — useful for checking a sync worked, or for branching a flow on an attribute Customer.io holds.
Input Parameters
| Parameter | Variable | Required | Description |
|---|---|---|---|
{{email}} | Yes (Look Up Person by Email) | Exact email address to search for — this is not a partial match | |
| ID | {{id}} | Yes (Get Person Attributes) | The person's identifier in Customer.io |
Output
| Field | Description |
|---|---|
| results | Look Up Person by Email — the matching people |
| customer | Get Person Attributes — the person with every attribute stored on them |
results is an array. Flows cannot resolve array indexes, so add a Loop stage over the response if you need to read individual people out of it.
Important Notes
- The same email address can belong to more than one person if your workspace has been fed inconsistent identifiers, which is why this returns an array rather than a single record
- Get Person Attributes returns custom attributes alongside standard ones, so it is the quickest way to confirm a Create or Update Person payload landed as intended
- Get Person Attributes also returns
cio_id, a permanent identifier Customer.io generates automatically and never changes. If you ever need to update a person's email or other identifier after it's been set, reference them usingcio_<the cio_id value>in the ID field of another stage — this is the only way to change an identifier once it exists
List Campaigns / List Broadcasts / List Segments / List Transactional Messages
These four stages take no parameters and return everything of their type in the workspace. Their purpose is to find the IDs the other stages need.
Output
| Stage | Field | Feeds |
|---|---|---|
| List Campaigns | campaigns | Reference only |
| List Broadcasts | broadcasts | Trigger Broadcast |
| List Segments | segments | Add to Segment / Remove from Segment |
| List Transactional Messages | messages | Send Transactional Email / Push |
Each of these fields is an array. Flows cannot resolve array indexes — add a Loop stage over the response, or run the stage once by hand and copy the ID you need into the stage that consumes it.
Important Notes
- In practice these are run once during setup to collect IDs, not on every flow execution
Use Cases
- Registration sync — a registration event fires Create or Update Person with the player's ID and a Payload mapping their registration fields onto your Customer.io attributes. Customer.io journeys take over from there.
- Behavioural campaign triggers — call Track Event with
first_depositorkyc_verifiedand let Customer.io campaigns react, with no further logic in the Flow. - Profile enrichment over time — call Create or Update Person again whenever a player's tier, balance band or preferences change. Attributes merge, so each call only needs the fields that changed.
- Transactional receipts — after a payment, Send Transactional Email with your receipt template and the player's ID as Identifier ID.
- Launch a journey from a Flow — run List Broadcasts once to find the ID, then call Trigger Broadcast whenever your Flow's conditions are met.
- Unsubscribe and GDPR handling — Suppress Person on an opt-out, Delete Person on an erasure request.
- Mobile push lifecycle — Add or Update Device on every app launch, Delete Device on logout, then Send Transactional Push for time-critical alerts.
How to Test
- Install Customer.io from the marketplace and create a connection.
- Paste your Site ID, Track API Key and App API Key from Settings > Account Settings > API Credentials into the three connection parameters. They must come from an EU workspace.
- Start with List Segments — it takes no parameters and needs nothing pre-existing, so it proves the App API Key works on its own.
- Run Create or Update Person with
id=flows-test-1andpayload={"email":"[email protected]","first_name":"Jane","plan":"pro"}. Open People in Customer.io and confirm the person exists with all three attributes — this proves the Track API credentials work. - Run it again with
payload={"plan":"enterprise"}and confirm onlyplanchanged whileemailandfirst_namesurvived. This is the merge behaviour. - Run Track Event with the same id and
eventName=flows_test_event, then check the person's Activity log. - Run Get Person Attributes with that id to confirm the read path and the App API Key.
- Clean up with Delete Person.
Each endpoint also carries a pink note in the Designer canvas with its own test recipe and example values.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| 401 on Create or Update Person, Track Event or any other data-push stage | Wrong Site ID or Track API Key, or credentials from a US workspace | Re-copy both from the Track API Keys tab of an EU workspace. This App only talks to the EU data centre |
| 401 on sends, triggers or List stages | Wrong, revoked or US-region App API Key | Create a new App API Key in the EU workspace and paste it into the connection |
| Person appears but never receives anything | The profile has no email address, or the person is suppressed | Include email in the Payload, and check the person is not suppressed |
| 400 on Create or Update Person | The Payload is not a valid JSON object | Check for trailing commas, unquoted keys and unescaped quotes inside values. A malformed object is rejected wholesale rather than partially applied |
| 404 on Trigger Broadcast | The broadcast is not API-triggered, or is still a draft | Confirm the ID with List Broadcasts and check the broadcast type in Customer.io |
| 400 on a transactional send | The template does not exist, is a draft, or requires variables that were not supplied | Confirm the ID with List Transactional Messages and check the template's required variables |
| Transactional push returns a delivery_id but nothing arrives | The person has no device registered | Call Add or Update Device with the handset's push token first |
| Add to Segment succeeds but membership does not change | The segment is data-driven, or the person IDs do not exist | Use a manual segment, and confirm the IDs with Look Up Person by Email |
| Duplicate people in Customer.io | The same person was sent under two different identifier types | Pick one identifier — your own user ID or email — and use it consistently in every stage |