AGENT SKILL

customerio-sdk-patterns

Apply production-ready Customer.io SDK patterns. Use when implementing best practices, refactoring integrations, or optimizing Customer.io usage in your application. Trigger with phrases like "customer.io best practices", "customer.io patterns", "production customer.io", "customer.io architecture".

License 许可
MIT
最近更新
2026-11-13
Stars
★ 2.0k
Forks
233

01 前言 Front Matter

name
customerio-sdk-patterns
author
Jeremy Longshore <jeremy@intentsolutions.io>
license
MIT
version
1.0.0
description
Apply production-ready Customer.io SDK patterns. Use when implementing best practices, refactoring integrations, or optimizing Customer.io usage in your application. Trigger with phrases like "customer.io best practices", "customer.io patterns", "production customer.io", "customer.io architecture".
allowed-tools
Read, Write, Edit, Bash(npm:*), Bash(pip:*), Grep

03 SKILL.md 正文

Customer.io SDK Patterns

Overview

Production-ready patterns for Customer.io SDK usage including error handling, batching, and type safety.

Prerequisites

  • Customer.io SDK installed
  • TypeScript project (recommended)
  • Understanding of async/await patterns

Instructions

Pattern 1: Type-Safe Client

// types/customerio.ts
export interface UserAttributes {
  email: string;
  first_name?: string;
  last_name?: string;
  created_at?: number;
  plan?: 'free' | 'pro' | 'enterprise';
  [key: string]: string | number | boolean | undefined;
}

export interface EventData {
  [key: string]: string | number | boolean | object;
}

export type EventName =
  | 'signed_up'
  | 'subscription_started'
  | 'subscription_cancelled'
  | 'feature_used'
  | 'email_verified';

// lib/customerio-client.ts
import { TrackClient, RegionUS } from '@customerio/track';
import type { UserAttributes, EventData, EventName } from '../types/customerio';

export class TypedCustomerIO {
  private client: TrackClient;

  constructor() {
    this.client = new TrackClient(
      process.env.CUSTOMERIO_SITE_ID!,
      process.env.CUSTOMERIO_API_KEY!,
      { region: RegionUS }
    );
  }

  async identify(userId: string, attributes: UserAttributes): Promise<void> {
    await this.client.identify(userId, {
      ...attributes,
      _updated_at: Math.floor(Date.now() / 1000)
    });
  }

  async track(userId: string, event: EventName, data?: EventData): Promise<void> {
    await this.client.track(userId, { name: event, data });
  }
}

Pattern 2: Retry with Exponential Backoff

// lib/customerio-resilient.ts
import { TrackClient } from '@customerio/track';

interface RetryConfig {
  maxRetries: number;
  baseDelay: number;
  maxDelay: number;
}

const defaultRetryConfig: RetryConfig = {
  maxRetries: 3,
  baseDelay: 1000,
  maxDelay: 10000
};

async function withRetry<T>(
  operation: () => Promise<T>,
  config: RetryConfig = defaultRetryConfig
): Promise<T> {
  let lastError: Error | undefined;

  for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error as Error;

      if (attempt === config.maxRetries) break;

      // Don't retry on 4xx errors (client errors)
      if (error instanceof Error && error.message.includes('4')) {
        throw error;
      }

      const delay = Math.min(
        config.baseDelay * Math.pow(2, attempt),
        config.maxDelay
      );
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

export class ResilientCustomerIO {
  private client: TrackClient;

  constructor(siteId: string, apiKey: string) {
    this.client = new TrackClient(siteId, apiKey, { region: RegionUS });
  }

  async identify(userId: string, attributes: Record<string, any>) {
    return withRetry(() => this.client.identify(userId, attributes));
  }

  async track(userId: string, event: string, data?: Record<string, any>) {
    return withRetry(() => this.client.track(userId, { name: event, data }));
  }
}

Pattern 3: Event Queue with Batching

// lib/customerio-queue.ts
interface QueuedEvent {
  userId: string;
  event: string;
  data?: Record<string, any>;
  timestamp: number;
}

export class CustomerIOQueue {
  private queue: QueuedEvent[] = [];
  private flushInterval: NodeJS.Timer | null = null;
  private maxBatchSize = 100;
  private flushIntervalMs = 5000;

  constructor(private client: TrackClient) {
    this.startAutoFlush();
  }

  enqueue(userId: string, event: string, data?: Record<string, any>) {
    this.queue.push({
      userId,
      event,
      data,
      timestamp: Date.now()
    });

    if (this.queue.length >= this.maxBatchSize) {
      this.flush();
    }
  }

  async flush(): Promise<void> {
    if (this.queue.length === 0) return;

    const batch = this.queue.splice(0, this.maxBatchSize);

    await Promise.allSettled(
      batch.map(item =>
        this.client.track(item.userId, {
          name: item.event,
          data: { ...item.data, _queued_at: item.timestamp }
        })
      )
    );
  }

  private startAutoFlush() {
    this.flushInterval = setInterval(() => this.flush(), this.flushIntervalMs);
  }

  async shutdown(): Promise<void> {
    if (this.flushInterval) {
      clearInterval(this.flushInterval);
    }
    await this.flush();
  }
}

Pattern 4: Singleton with Lazy Initialization

// lib/customerio-singleton.ts
import { TrackClient, RegionUS } from '@customerio/track';

let instance: TrackClient | null = null;

export function getCustomerIO(): TrackClient {
  if (!instance) {
    if (!process.env.CUSTOMERIO_SITE_ID || !process.env.CUSTOMERIO_API_KEY) {
      throw new Error('Customer.io credentials not configured');
    }
    instance = new TrackClient(
      process.env.CUSTOMERIO_SITE_ID,
      process.env.CUSTOMERIO_API_KEY,
      { region: RegionUS }
    );
  }
  return instance;
}

// Usage
import { getCustomerIO } from './lib/customerio-singleton';
await getCustomerIO().identify('user-123', { email: 'user@example.com' });

Output

  • Type-safe Customer.io client
  • Resilient error handling with retries
  • Event batching for high-volume scenarios
  • Singleton pattern for resource efficiency

Error Handling

Error Cause Solution
Type mismatch Invalid attribute type Use TypeScript interfaces
Queue overflow Too many events Increase flush frequency or batch size
Retry exhausted Persistent failure Check network and credentials

Resources

Next Steps

After implementing patterns, proceed to customerio-primary-workflow to implement messaging workflows.

04 同仓库更多 Skills

Dicklesworthstone
Dicklesworthstone/pi_agent_rust

web-artifacts-builder

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

前端开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

webapp-testing

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

测试与质量前端开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

neuropixels-analysis

Neuropixels neural recording analysis. Load SpikeGLX/OpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen/IBL curation, AI-assisted visual analysis, for Neuropixels 1.0/2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.

★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

skill-creator

Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.

Skill与智能体开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

notebooklm

Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.

Skill与智能体开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

slack-gif-creator

Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."

邮件与沟通图像与多媒体
★ 2.0k