Customer.io Hello World
Overview
Create a minimal working Customer.io integration: identify a user (create/update their profile), track an event, and send a transactional email. This covers the three fundamental Customer.io operations.
Prerequisites
customerio-nodeinstalled (npm install customerio-node)CUSTOMERIO_SITE_IDandCUSTOMERIO_TRACK_API_KEYconfiguredCUSTOMERIO_APP_API_KEYconfigured (for transactional email example)
Instructions
Step 1: Identify a User (Create/Update Profile)
// hello-customerio.ts
import { TrackClient, RegionUS } from "customerio-node";
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
// identify() creates the user if they don't exist, or updates if they do.
// The first argument is your internal user ID (immutable — use DB primary key).
await cio.identify("user-123", {
email: "hello@example.com", // Required for email campaigns
first_name: "Jane",
last_name: "Doe",
plan: "pro",
created_at: Math.floor(Date.now() / 1000), // Unix seconds, NOT milliseconds
});
console.log("User identified in Customer.io");
Key rules:
id(first arg) should be your immutable database ID — never use email as IDemailattribute is required if you want to send email campaignscreated_atmust be Unix timestamp in seconds (not ms) —Math.floor(Date.now() / 1000)- All custom attributes are stored on the user profile and usable in segments + Liquid templates
Step 2: Track an Event
// Track a custom event on the user's activity timeline.
// Events trigger campaigns — the event name must match exactly in the dashboard.
await cio.track("user-123", {
name: "signed_up", // snake_case, matches campaign trigger
data: {
signup_method: "google_oauth",
referral_source: "product_hunt",
timestamp: Math.floor(Date.now() / 1000),
},
});
console.log("Event tracked in Customer.io");
Key rules:
- User must be identified before tracking events (call
identify()first) - Event
nameis case-sensitive and must match your campaign trigger exactly - Use
snake_casefor event names —signed_up, notSigned UporsignedUp dataproperties are accessible in Liquid templates as{{ event.property_name }}
Step 3: Track an Anonymous Event
// Track events before the user signs up — merge later on identification
await cio.trackAnonymous({
anonymous_id: "anon-abc-123", // Your anonymous tracking ID (cookie, device ID)
name: "page_viewed",
data: {
url: "/pricing",
referrer: "https://google.com",
},
});
console.log("Anonymous event tracked");
When the anonymous user signs up, include anonymous_id in the identify() call to merge their pre-signup activity:
await cio.identify("user-123", {
email: "hello@example.com",
anonymous_id: "anon-abc-123", // Merges anonymous activity
});
Step 4: Send a Transactional Email
import { APIClient, SendEmailRequest, RegionUS } from "customerio-node";
const api = new APIClient(process.env.CUSTOMERIO_APP_API_KEY!, {
region: RegionUS,
});
const request = new SendEmailRequest({
to: "hello@example.com",
transactional_message_id: "1", // ID from Customer.io dashboard
message_data: { // Populates {{ liquid }} variables
welcome_name: "Jane",
login_url: "https://app.example.com/login",
},
identifiers: { id: "user-123" }, // Links delivery to user profile
});
const response = await api.sendEmail(request);
console.log("Email queued:", response.delivery_id);
Step 5: Verify in Dashboard
- Go to https://fly.customer.io
- Navigate to People and search for "hello@example.com"
- Verify the profile shows
first_name,plan, and other attributes - Click the Activity tab to see the
signed_upevent - Check Deliveries for the transactional email
Complete Example
// scripts/hello-customerio.ts
import {
TrackClient, APIClient, SendEmailRequest, RegionUS
} from "customerio-node";
async function main() {
// Track API client — identify and track
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
// 1. Identify
await cio.identify("user-hello-world", {
email: "hello@example.com",
first_name: "Jane",
created_at: Math.floor(Date.now() / 1000),
});
console.log("1. User identified");
// 2. Track event
await cio.track("user-hello-world", {
name: "hello_world_completed",
data: { sdk: "customerio-node", step: "quickstart" },
});
console.log("2. Event tracked");
// 3. Clean up test user (optional)
await cio.suppress("user-hello-world");
console.log("3. Test user suppressed (won't receive messages)");
}
main().catch(console.error);
Run: npx tsx scripts/hello-customerio.ts
Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| 401 Unauthorized | Invalid credentials | Verify Site ID + Track API Key in dashboard |
| 400 Bad Request | Malformed payload | Check attribute types and event name format |
| User not in People tab | identify() not called | Always call identify() before track() |
| Event not in Activity | Dashboard propagation delay | Wait 1-2 minutes and refresh |
| Transactional email fails | Wrong transactional_message_id | Verify the ID matches your template in Customer.io |
Resources
Next Steps
After verifying hello world works, proceed to customerio-local-dev-loop to set up your development workflow.