customerio-cost-tuning

Optimize Customer.io costs and usage. Use when reducing expenses, optimizing usage, or right-sizing your Customer.io plan. Trigger with phrases like "customer.io cost", "reduce customer.io spend", "customer.io billing", "customer.io pricing". allowed-tools: Read, Write, Edit, Bash(gh:*), Bash(curl:*) version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>

Allowed Tools

No tools specified

Provided by Plugin

customerio-pack

Claude Code skill pack for Customer.io (24 skills)

saas packs v1.0.0
View Plugin

Installation

This skill is included in the customerio-pack plugin:

/plugin install customerio-pack@claude-code-plugins-plus

Click to copy

Instructions

# Customer.io Cost Tuning ## Overview Optimize Customer.io costs by managing profiles, reducing unnecessary operations, and right-sizing your usage. ## Prerequisites - Access to Customer.io billing dashboard - Understanding of pricing model - API access for usage analysis ## Customer.io Pricing Model | Component | Pricing Basis | |-----------|---------------| | Profiles | Number of people tracked | | Emails | Volume sent (included amount varies) | | SMS | Per message sent | | Push | Volume sent | | Objects | Included with plan | ## Instructions ### Step 1: Profile Cleanup ```typescript // scripts/profile-audit.ts import { APIClient, RegionUS } from '@customerio/track'; interface ProfileAudit { total: number; inactive30Days: number; inactive90Days: number; noEmail: number; suppressed: number; recommendations: string[]; } async function auditProfiles(): Promise { const audit: ProfileAudit = { total: 0, inactive30Days: 0, inactive90Days: 0, noEmail: 0, suppressed: 0, recommendations: [] }; // Query via Customer.io App API or export // Analyze profile data const now = Math.floor(Date.now() / 1000); const thirtyDaysAgo = now - (30 * 24 * 60 * 60); const ninetyDaysAgo = now - (90 * 24 * 60 * 60); // Example analysis if (audit.inactive90Days > audit.total * 0.3) { audit.recommendations.push( 'Consider archiving profiles inactive >90 days to reduce costs' ); } if (audit.noEmail > audit.total * 0.1) { audit.recommendations.push( 'Remove profiles without email addresses (cannot receive communications)' ); } return audit; } ``` ### Step 2: Suppress Inactive Users ```typescript // lib/profile-management.ts import { TrackClient, RegionUS } from '@customerio/track'; const client = new TrackClient( process.env.CUSTOMERIO_SITE_ID!, process.env.CUSTOMERIO_API_KEY!, { region: RegionUS } ); // Suppress users who haven't engaged async function suppressInactiveUsers( userIds: string[], dryRun: boolean = true ): Promise<{ suppressed: string[]; errors: string[] }> { const results = { suppressed: [] as string[], errors: [] as string[] }; for (const userId of userIds) { if (dryRun) { console.log(`[DRY RUN] Would suppress: ${userId}`); results.suppressed.push(userId); continue; } try { await client.suppress(userId); results.suppressed.push(userId); } catch (error: any) { results.errors.push(`${userId}: ${error.message}`); } } return results; } // Delete users to fully remove from billing async function deleteUsers( userIds: string[], dryRun: boolean = true ): Promise<{ deleted: string[]; errors: string[] }> { const results = { deleted: [] as string[], errors: [] as string[] }; for (const userId of userIds) { if (dryRun) { console.log(`[DRY RUN] Would delete: ${userId}`); results.deleted.push(userId); continue; } try { await client.destroy(userId); results.deleted.push(userId); } catch (error: any) { results.errors.push(`${userId}: ${error.message}`); } } return results; } ``` ### Step 3: Event Deduplication ```typescript // lib/smart-tracking.ts import { LRUCache } from 'lru-cache'; import { TrackClient } from '@customerio/track'; const recentEvents = new LRUCache({ max: 100000, ttl: 3600000 // 1 hour }); interface TrackingConfig { dedupWindowMs: number; skipEvents: string[]; sampleRate: Record; } const config: TrackingConfig = { dedupWindowMs: 60000, // 1 minute dedup window skipEvents: [ 'page_viewed', // High volume, low value 'heartbeat' ], sampleRate: { 'feature_used': 0.1, // Sample 10% of feature usage 'search_performed': 0.5 // Sample 50% of searches } }; export function shouldTrackEvent( userId: string, eventName: string, data?: Record ): boolean { // Skip excluded events if (config.skipEvents.includes(eventName)) { return false; } // Apply sampling for high-volume events const sampleRate = config.sampleRate[eventName]; if (sampleRate !== undefined && Math.random() > sampleRate) { return false; } // Deduplicate identical events const eventKey = `${userId}:${eventName}:${JSON.stringify(data || {})}`; if (recentEvents.has(eventKey)) { return false; } recentEvents.set(eventKey, Date.now()); return true; } ``` ### Step 4: Email Cost Optimization ```typescript // lib/email-optimization.ts interface EmailOptimizationConfig { // Skip transactional emails for users who never open skipInactiveAfterDays: number; // Consolidate multiple notifications batchNotifications: boolean; batchWindowMinutes: number; // Suppress bounced emails suppressAfterBounces: number; } const emailConfig: EmailOptimizationConfig = { skipInactiveAfterDays: 180, // Skip users inactive 6 months batchNotifications: true, batchWindowMinutes: 30, suppressAfterBounces: 3 }; // Check if user should receive emails async function shouldSendEmail( userId: string, emailType: 'transactional' | 'marketing' ): Promise { // Always send critical transactional (password reset, security) if (emailType === 'transactional') { return true; } // Check engagement history const user = await getUserMetrics(userId); // Skip users who haven't opened in 6 months const sixMonthsAgo = Date.now() - (180 * 24 * 60 * 60 * 1000); if (user.lastEmailOpenedAt < sixMonthsAgo) { return false; } // Skip users with high bounce count if (user.bounceCount >= emailConfig.suppressAfterBounces) { return false; } return true; } ``` ### Step 5: Usage Monitoring Dashboard ```typescript // lib/usage-monitor.ts interface UsageMetrics { period: string; profiles: { total: number; new: number; deleted: number; }; events: { total: number; byType: Record; }; emails: { sent: number; delivered: number; opened: number; bounced: number; }; estimatedCost: number; } async function getUsageMetrics( startDate: Date, endDate: Date ): Promise { // Query Customer.io Reporting API // or aggregate from your tracking return { period: `${startDate.toISOString()} - ${endDate.toISOString()}`, profiles: { total: 10000, new: 500, deleted: 100 }, events: { total: 50000, byType: { 'signed_up': 500, 'feature_used': 20000, 'page_viewed': 25000 // Candidate for sampling } }, emails: { sent: 15000, delivered: 14500, opened: 4350, bounced: 150 }, estimatedCost: 299 // Monthly estimate }; } // Alert on unexpected usage spikes function checkUsageAlerts(metrics: UsageMetrics): string[] { const alerts: string[] = []; // Profile growth alert if (metrics.profiles.new > metrics.profiles.total * 0.1) { alerts.push('Unusual profile growth detected'); } // Event volume alert if (metrics.events.total > 100000) { alerts.push('High event volume - consider sampling'); } // Bounce rate alert if (metrics.emails.bounced / metrics.emails.sent > 0.05) { alerts.push('High bounce rate - clean email list'); } return alerts; } ``` ### Step 6: Cost Reduction Checklist ```markdown ## Monthly Cost Review Checklist ### Profile Optimization - [ ] Remove profiles with no email - [ ] Archive inactive profiles (>90 days) - [ ] Suppress hard bounced emails - [ ] Merge duplicate profiles ### Event Optimization - [ ] Identify high-volume, low-value events - [ ] Implement sampling for analytics events - [ ] Deduplicate redundant events - [ ] Remove deprecated event types ### Email Optimization - [ ] Clean suppression list - [ ] Re-engage or remove inactive subscribers - [ ] Consolidate notification emails - [ ] Optimize send frequency ### Plan Optimization - [ ] Review plan vs actual usage - [ ] Consider annual billing for discount - [ ] Evaluate feature usage vs plan tier ``` ## Cost Savings Estimates | Optimization | Typical Savings | |--------------|-----------------| | Profile cleanup | 10-30% | | Event deduplication | 5-15% | | Email list hygiene | 5-10% | | Sampling high-volume events | 10-20% | | Annual billing | 10-20% | ## Error Handling | Issue | Solution | |-------|----------| | Accidental deletion | Customer.io has 30-day recovery | | Over-suppression | Track suppression reasons | | Usage spike | Set up usage alerts | ## Resources - [Customer.io Pricing](https://customer.io/pricing/) - [Profile Management API](https://customer.io/docs/api/track/#operation/destroy) ## Next Steps After cost optimization, proceed to `customerio-reference-architecture` for architecture patterns.

Skill file: plugins/saas-packs/customerio-pack/skills/customerio-cost-tuning/SKILL.md