Customer.io Core Features
Overview
Implement Customer.io's key platform features: transactional emails/push (password resets, receipts), API-triggered broadcasts (one-to-many on demand), segment-driving attributes, anonymous-to-known user merging, and person suppression/deletion.
Prerequisites
customerio-node installed
- Track API credentials (
CUSTOMERIO_SITE_ID + CUSTOMERIO_TRACK_API_KEY)
- App API credential (
CUSTOMERIO_APP_API_KEY) — required for transactional + broadcasts
Instructions
Feature 1: Transactional Email
Transactional messages are opt-in-implied messages (receipts, password resets). Create the template in Customer.io dashboard first, then call the API with data.
import { APIClient, SendEmailRequest, RegionUS } from "customerio-node";
const api = new APIClient(process.env.CUSTOMERIO_APP_API_KEY!, {
region: RegionUS,
});
async function sendPasswordReset(email: string, userId: string, resetUrl: string) {
const request = new SendEmailRequest({
to: email,
transactional_message_id: "3",
message_data: {
reset_url: resetUrl,
expiry_hours: 24,
support_email: "help@yourapp.com",
},
identifiers: { id: userId },
});
const response = await api.sendEmail(request);
return response;
}
async function sendOrderConfirmation(
email: string,
userId: string,
order: { id: string; items: { name: string; qty: number; price: number }[]; total: number }
) {
const request = new SendEmailRequest({
to: email,
transactional_message_id: "5",
message_data: {
order_id: order.id,
items: order.items,
total: order.total,
order_date: new Date().toISOString(),
},
identifiers: { id: userId },
});
return api.sendEmail(request);
}
Dashboard setup: Create transactional message at Transactional > Create Message. Use Liquid syntax: {{ data.reset_url }}, {{ data.order_id }}, {% for item in data.items %}.
Feature 2: Transactional Push
import { APIClient, SendPushRequest, RegionUS } from "customerio-node";
const api = new APIClient(process.env.CUSTOMERIO_APP_API_KEY!, {
region: RegionUS,
});
async function sendPushNotification(userId: string, title: string, body: string) {
const request = new SendPushRequest({
transactional_message_id: "7",
identifiers: { id: userId },
message_data: { title, body },
});
return api.sendPush(request);
}
Feature 3: API-Triggered Broadcasts
Broadcasts send to a pre-defined segment on demand. Define the segment and message in the dashboard, then trigger via API.
import { APIClient, RegionUS } from "customerio-node";
const api = new APIClient(process.env.CUSTOMERIO_APP_API_KEY!, {
region: RegionUS,
});
async function triggerProductUpdateBroadcast(version: string, changelog: string) {
await api.triggerBroadcast(
42,
{ version, changelog },
{ segment: { id: 15 } }
);
}
async function triggerBroadcastToEmails(
broadcastId: number,
emails: string[],
data: Record<string, any>
) {
await api.triggerBroadcast(
broadcastId,
data,
{
emails,
email_ignore_missing: true,
: ,
}
);
}
() {
api.(broadcastId, data, { : userIds });
}
Feature 4: Segment-Driving Attributes
Segments in Customer.io are data-driven — they automatically include/exclude people based on attributes you set via identify().
import { TrackClient, RegionUS } from "customerio-node";
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
async function updateEngagementAttributes(userId: string, metrics: {
lastActiveAt: Date;
sessionCount: number;
totalRevenue: number;
daysSinceLastLogin: number;
}) {
await cio.identify(userId, {
last_active_at: Math.floor(metrics.lastActiveAt.getTime() / 1000),
session_count: metrics.sessionCount,
total_revenue: metrics.totalRevenue,
days_since_last_login: metrics.daysSinceLastLogin,
engagement_tier: metrics.sessionCount > 50 ? "power_user"
: metrics.sessionCount > 10 ? "active"
: "casual",
});
}
Feature 5: Merge Duplicate People
async function mergeUsers(
primaryId: string,
secondaryId: string,
identifierType: "id" | "email" | "cio_id" = "id"
) {
await cio.mergeCustomers(
identifierType,
primaryId,
identifierType,
secondaryId
);
}
Feature 6: Suppress and Delete People
async function suppressUser(userId: string): Promise<void> {
await cio.suppress(userId);
}
async function deleteUser(userId: string): Promise<void> {
await cio.destroy(userId);
}
Error Handling
| Error | Cause | Solution |
|---|
422 on transactional | Wrong transactional_message_id | Verify template ID in Customer.io dashboard |
| Missing Liquid variable | message_data incomplete | Ensure all {{ data.x }} variables are in message_data |
Broadcast 404 | Invalid broadcast ID | Check broadcast ID in dashboard Broadcasts section |
| Segment not updating | Attribute type mismatch | Dashboard segments compare types strictly — number vs string matters |
| Merge fails | Secondary person doesn't exist | Verify both people exist before merging |
Resources
Next Steps
After implementing core features, proceed to customerio-common-errors for troubleshooting.