Claude-skill-registry customerio-sdk-patterns
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/customerio-sdk-patterns" ~/.claude/skills/majiayu000-claude-skill-registry-customerio-sdk-patterns && rm -rf "$T"
manifest:
skills/data/customerio-sdk-patterns/SKILL.mdsource content
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.