Claude-code-plugins-plus customerio-core-feature
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/customerio-pack/skills/customerio-core-feature" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-customerio-core-feature && rm -rf "$T"
plugins/saas-packs/customerio-pack/skills/customerio-core-feature/SKILL.mdCustomer.io Core Features
Overview
Implement Customer.io's key platform features: transactional emails/push (password resets, receipts), API-triggered broadcasts (one-to-many on demand), segment-driving attributes, anonymous-to-known user merging, and person suppression/deletion.
Prerequisites
installedcustomerio-node- Track API credentials (
+CUSTOMERIO_SITE_ID
)CUSTOMERIO_TRACK_API_KEY - App API credential (
) — required for transactional + broadcastsCUSTOMERIO_APP_API_KEY
Instructions
Feature 1: Transactional Email
Transactional messages are opt-in-implied messages (receipts, password resets). Create the template in Customer.io dashboard first, then call the API with data.
// lib/customerio-transactional.ts import { APIClient, SendEmailRequest, RegionUS } from "customerio-node"; const api = new APIClient(process.env.CUSTOMERIO_APP_API_KEY!, { region: RegionUS, }); // Send a transactional email // transactional_message_id comes from the Customer.io dashboard template async function sendPasswordReset(email: string, userId: string, resetUrl: string) { const request = new SendEmailRequest({ to: email, transactional_message_id: "3", // Template ID from dashboard message_data: { reset_url: resetUrl, expiry_hours: 24, support_email: "help@yourapp.com", }, identifiers: { id: userId }, // Links delivery metrics to user profile }); const response = await api.sendEmail(request); // response = { delivery_id: "abc123", queued_at: 1704067200 } return response; } // Send an order confirmation with complex data async function sendOrderConfirmation( email: string, userId: string, order: { id: string; items: { name: string; qty: number; price: number }[]; total: number } ) { const request = new SendEmailRequest({ to: email, transactional_message_id: "5", message_data: { order_id: order.id, items: order.items, // Accessible as {{ event.items }} in Liquid total: order.total, order_date: new Date().toISOString(), }, identifiers: { id: userId }, }); return api.sendEmail(request); }
Dashboard setup: Create transactional message at Transactional > Create Message. Use Liquid syntax:
{{ data.reset_url }}, {{ data.order_id }}, {% for item in data.items %}.
Feature 2: Transactional Push
import { APIClient, SendPushRequest, RegionUS } from "customerio-node"; const api = new APIClient(process.env.CUSTOMERIO_APP_API_KEY!, { region: RegionUS, }); async function sendPushNotification(userId: string, title: string, body: string) { const request = new SendPushRequest({ transactional_message_id: "7", // Push template ID identifiers: { id: userId }, message_data: { title, body }, }); return api.sendPush(request); }
Feature 3: API-Triggered Broadcasts
Broadcasts send to a pre-defined segment on demand. Define the segment and message in the dashboard, then trigger via API.
// lib/customerio-broadcasts.ts import { APIClient, RegionUS } from "customerio-node"; const api = new APIClient(process.env.CUSTOMERIO_APP_API_KEY!, { region: RegionUS, }); // Trigger a broadcast to a pre-defined segment async function triggerProductUpdateBroadcast(version: string, changelog: string) { await api.triggerBroadcast( 42, // Broadcast ID from dashboard { version, changelog }, // Data merged into Liquid template { segment: { id: 15 } } // Target segment (defined in dashboard) ); } // Trigger broadcast to specific emails async function triggerBroadcastToEmails( broadcastId: number, emails: string[], data: Record<string, any> ) { await api.triggerBroadcast( broadcastId, data, { emails, email_ignore_missing: true, // Don't error on unknown emails email_add_duplicates: false, // Skip if user already in broadcast } ); } // Trigger broadcast to specific user IDs async function triggerBroadcastToUsers( broadcastId: number, userIds: string[], data: Record<string, any> ) { await api.triggerBroadcast(broadcastId, data, { ids: userIds }); }
Feature 4: Segment-Driving Attributes
Segments in Customer.io are data-driven — they automatically include/exclude people based on attributes you set via
identify().
// services/customerio-segments.ts import { TrackClient, RegionUS } from "customerio-node"; const cio = new TrackClient( process.env.CUSTOMERIO_SITE_ID!, process.env.CUSTOMERIO_TRACK_API_KEY!, { region: RegionUS } ); // Update attributes that drive segment membership async function updateEngagementAttributes(userId: string, metrics: { lastActiveAt: Date; sessionCount: number; totalRevenue: number; daysSinceLastLogin: number; }) { await cio.identify(userId, { last_active_at: Math.floor(metrics.lastActiveAt.getTime() / 1000), session_count: metrics.sessionCount, total_revenue: metrics.totalRevenue, days_since_last_login: metrics.daysSinceLastLogin, engagement_tier: metrics.sessionCount > 50 ? "power_user" : metrics.sessionCount > 10 ? "active" : "casual", }); } // Segment examples built in dashboard: // "Power Users": engagement_tier = "power_user" // "At Risk": days_since_last_login > 30 AND plan != "free" // "High Value": total_revenue > 500 // "Trial Expiring": plan = "trial" AND trial_end_at < now + 3 days
Feature 5: Merge Duplicate People
// Merge a secondary (duplicate) person into a primary person. // The secondary is deleted permanently after merge. async function mergeUsers( primaryId: string, secondaryId: string, identifierType: "id" | "email" | "cio_id" = "id" ) { await cio.mergeCustomers( identifierType, primaryId, identifierType, secondaryId ); // Secondary person is permanently deleted. // Their attributes and activity merge into the primary. }
Feature 6: Suppress and Delete People
// Suppress: user stays in system but receives no messages async function suppressUser(userId: string): Promise<void> { await cio.suppress(userId); } // Delete: remove user entirely from Customer.io async function deleteUser(userId: string): Promise<void> { await cio.destroy(userId); }
Error Handling
| Error | Cause | Solution |
|---|---|---|
on transactional | Wrong | Verify template ID in Customer.io dashboard |
| Missing Liquid variable | incomplete | Ensure all variables are in |
Broadcast | Invalid broadcast ID | Check broadcast ID in dashboard Broadcasts section |
| Segment not updating | Attribute type mismatch | Dashboard segments compare types strictly — number vs string matters |
| Merge fails | Secondary person doesn't exist | Verify both people exist before merging |
Resources
Next Steps
After implementing core features, proceed to
customerio-common-errors for troubleshooting.