Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create a minimal working Customer.io example. Use when learning Customer.io basics, testing SDK setup, or creating your first identify + track integration. Trigger: "customer.io hello world", "first customer.io message", "test customer.io", "customer.io example", "customer.io quickstart".
.claude/skills/jeremylongshore-customerio-hello-world/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 170% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 62% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 113% | 0% |
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)typescript// 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)typescript// 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 }}typescript// 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:
typescriptawait cio.identify("user-123", { email: "hello@example.com", anonymous_id: "anon-abc-123", // Merges anonymous activity });
typescriptimport { 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 eventtypescript// 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.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 8,770 | 6,460 | -26% | 1 | 1 | 0% | 1,857 | 3,216 | +73% | 0 | 0 | — |
case-02 | pass→pass | 13,171 | 8,254 | -37% | 1 | 1 | 0% | 2,623 | 3,329 | +27% | 0 | 0 | — |
case-03 | pass→pass | 6,222 | 2,978 | -52% | 1 | 1 | 0% | 1,145 | 2,221 | +94% | 0 | 0 | — |
case-04 | pass→pass | 4,405 | 4,063 | -8% | 1 | 1 | 0% | 942 | 2,572 | +173% | 0 | 0 | — |
case-22 | pass→pass | 10,794 | 12,048 | +12% | 1 | 1 | 0% | 2,240 | 4,136 | +85% | 0 | 0 | — |
case-05 | fail→pass | 5,308 | 4,929 | -7% | 1 | 1 | 0% | 973 | 2,628 | +170% | 0 | 0 | — |
case-06 | fail→pass | 8,493 | 5,464 | -36% | 1 | 1 | 0% | 1,769 | 2,866 | +62% | 0 | 0 | — |
case-07 | fail→pass | 9,343 | 6,398 | -32% | 1 | 1 | 0% | 1,841 | 3,081 | +67% | 0 | 0 | — |
case-08 | pass→pass | 9,885 | 4,541 | -54% | 1 | 1 | 0% | 1,932 | 2,601 | +35% | 0 | 0 | — |
case-09 | fail→pass | 6,557 | 4,632 | -29% | 1 | 1 | 0% | 1,188 | 2,527 | +113% | 0 | 0 | — |
case-10 | pass→pass | 9,593 | 8,667 | -10% | 1 | 1 | 0% | 1,847 | 3,437 | +86% | 0 | 0 | — |
case-11 | pass→pass | 8,525 | 5,920 | -31% | 1 | 1 | 0% | 1,839 | 2,903 | +58% | 0 | 0 | — |
case-12 | pass→pass | 5,463 | 2,037 | -63% | 1 | 1 | 0% | 990 | 2,049 | +107% | 0 | 0 | — |
case-13 | pass→pass | 13,231 | 9,685 | -27% | 1 | 1 | 0% | 2,570 | 3,629 | +41% | 0 | 0 | — |
case-14 | pass→pass | 12,525 | 10,828 | -14% | 1 | 1 | 0% | 2,200 | 3,773 | +72% | 0 | 0 | — |
case-15 | fail→pass | 9,766 | 3,252 | -67% | 1 | 1 | 0% | 1,578 | 2,243 | +42% | 0 | 0 | — |
case-16 | pass→pass | 5,936 | 2,122 | -64% | 1 | 1 | 0% | 1,122 | 2,026 | +81% | 0 | 0 | — |
case-17 | pass→pass | 4,093 | 4,473 | +9% | 1 | 1 | 0% | 863 | 2,595 | +201% | 0 | 0 | — |
case-18 | pass→pass | 6,195 | 4,364 | -30% | 1 | 1 | 0% | 1,169 | 2,545 | +118% | 0 | 0 | — |
case-19 | pass→pass | 8,274 | 3,758 | -55% | 1 | 1 | 0% | 1,619 | 2,445 | +51% | 0 | 0 | — |
case-20 | pass→pass | 12,389 | 17,254 | +39% | 1 | 1 | 0% | 2,624 | 5,402 | +106% | 0 | 0 | — |
case-21 | pass→pass | 8,059 | 9,893 | +23% | 1 | 1 | 0% | 1,590 | 3,559 | +124% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.