'Implement Customer.io primary messaging workflow.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill customerio-primary-workflow --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Customerio Primary Workflow?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-customerio-primary-workflow-tons-of-skills-marketplace)More formats (shields.io, HTML) on the badges page.
---
name: customerio-primary-workflow
description: '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".
'
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(npx:*), Glob, Grep
version: 1.14.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- customer-io
- workflow
- campaigns
- automation
compatibility: Designed for Claude Code
---
# Customer.io Primary Workflow
## Output
- A consent-aware workflow with explicit trigger, audience, data contract, message/template version, and owner.
- A synthetic validation receipt and a scoped rollback/disable action for unexpected delivery behavior.
## Examples
Build the workflow in development using one synthetic profile and a versioned event, confirm trigger conditions and suppression/consent rules, then promote through staging. Enable production through an approved canary audience; pause/disable the workflow if the audience or message behavior does not match the reviewed expectation.
## Overview
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.
## Prerequisites
- `customerio-node` configured with Track API credentials
- Campaigns created in Customer.io dashboard (triggered by events you define)
- Understanding of your user lifecycle stages
## How Campaigns Work
```
Your 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 email
```
Events tracked via the SDK trigger campaigns you build in the dashboard. The SDK sends the **data**; the dashboard defines the **workflow logic**.
## Instructions
### Step 1: Define Your Event Taxonomy
```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];
```
### Step 2: Build the Messaging Service
```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 },
});
}
}
```
### Step 3: Integrate into Application Routes
```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 });
});
```
### Step 4: Dashboard Campaign Configuration
In Customer.io dashboard, create campaigns triggered by these events:
**Onboarding Campaign:**
1. **Trigger:** Event `signed_up`
2. **Wait** 5 minutes
3. **Send** welcome email (use `{{ customer.first_name }}` and `{{ event.method }}` Liquid)
4. **Wait** 1 day
5. **Branch:** Is `email_verified` true?
- No → Send verification reminder
- Yes → Continue
6. **Wait** 3 days
7. **Send** feature tips email
8. **Wait** 7 days
9. **Branch:** Has `first_project_created` event?
- No → Send activation nudge
- Yes → End (move to engagement campaign)
**Cancellation Win-back Campaign:**
1. **Trigger:** Event `subscription_cancelled`
2. **Wait** 3 days
3. **Send** "We miss you" email with `{{ event.reason }}` Liquid variable
4. **Wait** 7 days
5. **Send** discount offer email
## Liquid Template Variables
| 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 Handling
| 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 |
## Resources
- [Campaigns Documentation](https://docs.customer.io/journeys/campaigns-in-customerio/)
- [Liquid Personalization](https://docs.customer.io/journeys/using-liquid/)
- [Custom Events](https://docs.customer.io/integrations/data-in/custom-events/)
## Next Steps
After implementing primary workflow, proceed to `customerio-core-feature` for transactional messages, segments, and broadcasts.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!