Skillshub customerio-hello-world
install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/jeremylongshore/claude-code-plugins-plus-skills/customerio-hello-world" ~/.claude/skills/comeonoliver-skillshub-customerio-hello-world && rm -rf "$T"
manifest:
skills/jeremylongshore/claude-code-plugins-plus-skills/customerio-hello-world/SKILL.mdsource content
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
installed (customerio-node
)npm install customerio-node
andCUSTOMERIO_SITE_ID
configuredCUSTOMERIO_TRACK_API_KEY
configured (for transactional email example)CUSTOMERIO_APP_API_KEY
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:
(first arg) should be your immutable database ID — never use email as IDid
attribute is required if you want to send email campaignsemail
must be Unix timestamp in seconds (not ms) —created_atMath.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
first)identify() - Event
is case-sensitive and must match your campaign trigger exactlyname - Use
for event names —snake_case
, notsigned_up
orSigned UpsignedUp
properties are accessible in Liquid templates asdata{{ 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
, and other attributesplan - Click the Activity tab to see the
eventsigned_up - 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 |
|---|---|---|
| Invalid credentials | Verify Site ID + Track API Key in dashboard |
| Malformed payload | Check attribute types and event name format |
| User not in People tab | not called | Always call before |
| Event not in Activity | Dashboard propagation delay | Wait 1-2 minutes and refresh |
| Transactional email fails | Wrong | 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.