Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement Customer.io primary messaging workflow. Use when setting up campaign triggers, welcome sequences, onboarding flows, or event-driven email automation. Trigger: "customer.io campaign", "customer.io workflow", "customer.io email automation", "customer.io messaging", "customer.io onboarding".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 15% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 100% | 0% |
Implement Customer.io's core messaging workflow: identify users with segment-ready attributes, track lifecycle events that trigger campaigns, and set up the data layer for automated onboarding, nurture, and re-engagement sequences.
customerio-node configured with Track API credentialsYour App (SDK) Customer.io Dashboard User
───────────── ──────────────────── ────
cio.identify(user) → Profile created/updated
cio.track("signed_up") → Campaign trigger fires
Wait 1 day → Welcome email
Check: verified?
├─ No → Verification reminder
└─ Yes → Wait 3 days → Feature tips emailEvents tracked via the SDK trigger campaigns you build in the dashboard. The SDK sends the data; the dashboard defines the workflow logic.
typescript// lib/customerio-events.ts import { TrackClient, RegionUS } from "customerio-node"; // Central event definitions — every event your app tracks export const CIO_EVENTS = { // Onboarding SIGNED_UP: "signed_up", EMAIL_VERIFIED: "email_verified", PROFILE_COMPLETED: "profile_completed", FIRST_PROJECT_CREATED: "first_project_created", // Engagement FEATURE_USED: "feature_used", INVITED_TEAMMATE: "invited_teammate", UPGRADE_STARTED: "upgrade_started", UPGRADE_COMPLETED: "upgrade_completed", // Lifecycle SUBSCRIPTION_RENEWED: "subscription_renewed", SUBSCRIPTION_CANCELLED: "subscription_cancelled", TRIAL_EXPIRING: "trial_expiring", // Commerce CHECKOUT_STARTED: "checkout_started", CHECKOUT_COMPLETED: "checkout_completed", REFUND_REQUESTED: "refund_requested", } as const; type EventName = (typeof CIO_EVENTS)[keyof typeof CIO_EVENTS];
typescript// services/customerio-messaging.ts import { TrackClient, RegionUS } from "customerio-node"; import { CIO_EVENTS } from "../lib/customerio-events"; const cio = new TrackClient( process.env.CUSTOMERIO_SITE_ID!, process.env.CUSTOMERIO_TRACK_API_KEY!, { region: RegionUS } ); interface UserProfile { id: string; email: string; firstName: string; lastName?: string; plan: string; companyName?: string; } export class MessagingService { /** Call on user signup — creates profile and triggers onboarding campaign */ async onSignup(user: UserProfile, signupMethod: string): Promise<void> { // 1. Identify with all attributes campaigns need await cio.identify(user.id, { email: user.email, first_name: user.firstName, last_name: user.lastName ?? "", plan: user.plan, company: user.companyName ?? "", created_at: Math.floor(Date.now() / 1000), onboarding_step: "signed_up", }); // 2. Track the event that triggers the onboarding campaign await cio.track(user.id, { name: CIO_EVENTS.SIGNED_UP, data: { method: signupMethod, // "google", "email", "github" plan: user.plan, }, }); } /** Call when user verifies email — updates profile + tracks event */ async onEmailVerified(userId: string): Promise<void> { await cio.identify(userId, { email_verified: true, email_verified_at: Math.floor(Date.now() / 1000), onboarding_step: "verified", }); await cio.track(userId, { name: CIO_EVENTS.EMAIL_VERIFIED, }); } /** Call on feature usage — drives engagement segments and campaigns */ async onFeatureUsed( userId: string, feature: string, metadata?: Record<string, any> ): Promise<void> { await cio.track(userId, { name: CIO_EVENTS.FEATURE_USED, data: { feature, ...metadata }, }); // Update engagement metrics on the profile for segmentation await cio.identify(userId, { last_active_at: Math.floor(Date.now() / 1000), }); } /** Call on plan upgrade — triggers upgrade confirmation campaign */ async onUpgrade(userId: string, from: string, to: string, mrr: number): Promise<void> { await cio.identify(userId, { plan: to, mrr, upgraded_at: Math.floor(Date.now() / 1000), }); await cio.track(userId, { name: CIO_EVENTS.UPGRADE_COMPLETED, data: { from_plan: from, to_plan: to, mrr }, }); } /** Call on cancellation — triggers win-back campaign */ async onCancellation(userId: string, reason: string): Promise<void> { await cio.identify(userId, { plan: "cancelled", cancelled_at: Math.floor(Date.now() / 1000), cancellation_reason: reason, }); await cio.track(userId, { name: CIO_EVENTS.SUBSCRIPTION_CANCELLED, data: { reason }, }); } }
typescript// routes/auth.ts (Express example) import { MessagingService } from "../services/customerio-messaging"; const messaging = new MessagingService(); router.post("/signup", async (req, res) => { const user = await db.createUser(req.body); // Fire-and-forget — don't block the signup response messaging.onSignup( { id: user.id, email: user.email, firstName: user.firstName, plan: user.plan, }, req.body.signupMethod ).catch((err) => console.error("CIO signup tracking failed:", err)); res.json({ user }); }); router.post("/verify-email", async (req, res) => { await db.verifyEmail(req.user.id); messaging.onEmailVerified(req.user.id).catch(console.error); res.json({ verified: true }); });
In Customer.io dashboard, create campaigns triggered by these events:
Onboarding Campaign:
signed_up{{ customer.first_name }} and {{ event.method }} Liquid)email_verified true?first_project_created event?Cancellation Win-back Campaign:
subscription_cancelled{{ event.reason }} Liquid variable| Variable | Source | Example | |----------|--------|---------| | {{ customer.first_name }} | identify() attributes | "Jane" | | {{ customer.plan }} | identify() attributes | "pro" | | {{ event.method }} | track() event data | "google" | | {{ event.reason }} | track() event data | "too_expensive" |
| Error | Cause | Solution | |-------|-------|----------| | Campaign not triggering | Event name mismatch | Event names are case-sensitive — verify exact match | | User not receiving email | Missing email attribute | Always include email in identify() | | Duplicate sends | Multiple event fires | Use fire-and-forget with deduplication | | Liquid rendering {{ }} | Missing data property | Ensure data object has all template variables |
After implementing primary workflow, proceed to customerio-core-feature for transactional messages, segments, and broadcasts.
Other measured skills in the registry, with their headline benchmark lift.