Customer.io
Sends person events, page views and identify calls to Customer.io’s Track API v2 entity endpoint, keyed by the Signal user_id.
Why route Customer.io through Datafly Signal
- Profiles are updated from the server, not the browser. Events reach Customer.io even when a tracker blocker or a failed page unload would have dropped a client-side call, so the profile reflects what actually happened.
- Presets for Retail and B2B / SaaS. The mapping starts from a working configuration for your vertical instead of a blank field map.
- One consent decision governs every destination. Customer.io is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.
Prerequisites
Find your Track API credentials
Workspace Settings > API and webhook credentials > Track API keys: copy the Site ID and API key.
Check your region
EU workspaces (fly-eu.customer.io) use track-eu.customer.io.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
site_id | string | Yes | Your Customer.io Site ID (Workspace Settings > API and webhook credentials > Track API keys). |
api_key | secret | Yes | The Track API key paired with the Site ID. Sent with it as HTTP Basic auth. |
track_host | select | Yes | The Track API host for your account’s region; EU workspaces must use track-eu.customer.io. Options: track.customer.io, track-eu.customer.io. |
Signal Setup
- Navigate to Integrations in the sidebar and open the Integration Library tab.
- Find Customer.io, click Install and fill in the fields above.
- Pick the preset for your vertical, then review the mappings in the blueprint editor.
curl -X POST http://localhost:8084/v1/admin/integration-catalog/customer_io/install \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer.io",
"config": {
"site_id": "abc123def456",
"api_key": "xxxxxxxxxxxxxxxxxxxx",
"track_host": "track.customer.io"
},
"delivery_mode": "server_side"
}'API endpoint
POST https://{track_host}/api/v2/entity
Authorization: Basic base64(site_id:api_key)Example body:
{"type": "person", "action": "event", "identifiers": {"id": "user-123"}, "name": "purchase",
"timestamp": 1758500000, "attributes": {"order_id": "T-1001", "value": 439.99, "currency": "GBP"}}The presets turn on the strict output contract: only the fields the blueprint maps are sent.
Event mapping
Retail preset (ecommerce)
Commerce events: page views, product viewed, product added, checkout started and purchase, with identify keeping the person’s email current. Sent through the Track API v2 entity endpoint, keyed by the Signal user_id as the person id (events without one are not sent). identify writes the person’s email in plain text, because Customer.io sends to it.
| Signal event | Sent to the vendor as |
|---|---|
identify | identify |
page | page |
view_item | product_viewed |
add_to_cart | product_added |
begin_checkout | checkout_started |
purchase | purchase |
B2B / SaaS preset (saas)
Product lifecycle: page views, sign-up, trial start, feature used, subscription created and cancelled, with identify keeping the person’s email current. Sent through the Track API v2 entity endpoint, keyed by the Signal user_id as the person id (events without one are not sent). identify writes the person’s email in plain text, because Customer.io sends to it.
| Signal event | Sent to the vendor as |
|---|---|
identify | identify |
page | page |
sign_up | signed_up |
trial_started | trial_started |
feature_used | feature_used |
subscription_started | subscription_created |
subscription_cancelled | subscription_cancelled |
Default preset (default)
Identify, page views, sign-up, lead and purchase. Sent through the Track API v2 entity endpoint, keyed by the Signal user_id as the person id (events without one are not sent). identify writes the person’s email in plain text, because Customer.io sends to it.
| Signal event | Sent to the vendor as |
|---|---|
identify | identify |
page | page |
sign_up | signed_up |
generate_lead | lead_submitted |
purchase | purchase |
Identity and personal data
The person is identified by identifiers.id, the Signal user_id; events without one are not sent. identify() writes the person’s email (trimmed, lowercased) as an attribute: the email the identify call carries, or, when it carries none, the email Signal has stored for the person (attached as $traits.email). The email is sent in plain text: Customer.io sends to it. Page views send the page URL as the name.
Consent
Customer.io events should be sent under the marketing consent category. Customer.io maintains its own opt-in records per channel (email, SMS, push) — Signal forwards CMP consent state on every event so journey logic can suppress messaging where appropriate.
Known limitations
- The Signal event ID is not sent: Customer.io only accepts a ULID for deduplication.
- Anonymous (pre-login) activity is not sent; Customer.io’s Pipelines API supports it and would need a separate integration.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
401 | Site ID / API key wrong, or the wrong region. |
400 | Missing identifier, or a page view without a URL. |