From customerio-pack
Creates minimal Customer.io Node.js integration: identify users, track events, send transactional emails via customerio-node SDK. For quickstarts, SDK testing, and basic setups.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin customerio-packThis skill is limited to using the following tools:
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.
Catalogs Customer.io pitfalls like wrong API keys, millisecond timestamps, track-before-identify, and email-as-ID. Use for integration reviews, onboarding, and code audits.
Generates minimal Klaviyo integration example using klaviyo-api Node.js SDK in TypeScript: create profiles, track events, query results.
Integrates Klaviyo email/SMS marketing API: manage profiles, track events, build flows, and segment customers. Use for e-commerce marketing features with Node.js, Python SDKs or direct HTTP.
Share bugs, ideas, or general feedback.
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.
customerio-node installed (npm install customerio-node)CUSTOMERIO_SITE_ID and CUSTOMERIO_TRACK_API_KEY configuredCUSTOMERIO_APP_API_KEY configured (for transactional email example)// 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 IDemail attribute is required if you want to send email campaignscreated_at must be Unix timestamp in seconds (not ms) — Math.floor(Date.now() / 1000)// 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:
identify() first)name is case-sensitive and must match your campaign trigger exactlysnake_case for event names — signed_up, not Signed Up or signedUpdata properties are accessible in Liquid templates as {{ event.property_name }}// 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
});
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);
first_name, plan, and other attributessigned_up event// 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 | 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 |
After verifying hello world works, proceed to customerio-local-dev-loop to set up your development workflow.