Skip to main contentcustomerio-cost-tuning
Optimize Customer.io costs and usage efficiency.
Use when reducing profile count, cleaning inactive users,
deduplicating events, or right-sizing your plan.
Trigger: "customer.io cost", "reduce customer.io spend",
"customer.io billing", "customer.io pricing", "customer.io cleanup".
설치로 이동 Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill customerio-cost-tuning명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.

jeremylongshore
jeremylongshore/claude-code-plugins-plus-skills
GitHub 저장소 열기| name | customerio-cost-tuning |
| description | Optimize Customer.io costs and usage efficiency.
Use when reducing profile count, cleaning inactive users,
deduplicating events, or right-sizing your plan.
Trigger: "customer.io cost", "reduce customer.io spend",
"customer.io billing", "customer.io pricing", "customer.io cleanup".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Bash(npx:*), Glob, Grep |
| version | 1.14.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
| tags | ["saas","customer-io","cost-optimization","billing"] |
| compatibility | Designed for Claude Code, also compatible with Codex and OpenClaw |
Customer.io Cost Tuning
Overview
Optimize Customer.io costs by managing profile count (the primary billing driver), suppressing/deleting inactive users, deduplicating events, reducing unnecessary API calls, and monitoring usage trends.
How Customer.io Pricing Works
Customer.io bills based on profile count (number of identified people in your workspace) and email/SMS volume. Key cost drivers:
| Factor | Impact | Optimization Strategy |
|---|
| Total profiles | Primary cost driver | Delete inactive profiles |
| Email sends | Per-email cost above tier | Suppress unengaged users |
| SMS sends | Per-SMS cost | Only send to opt-in users |
| Overidentification | Creates unnecessary profiles | Don't identify users who'll never receive messages |
| Event volume | Can increase processing costs | Deduplicate and sample |
Instructions
Step 1: Profile Audit
import { TrackClient, RegionUS } from "customerio-node";
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
const AUDIT_RULES = {
noEmail: "Don't identify users without email unless using push/SMS",
: ,
: ,
: ,
};
.();
( [rule, action] .()) {
.();
}
.();
.();
.();
.();
testUsers
"Suppress and delete test-*, ci-*, dev-* prefixed users"
staleAnonymous
"Delete anonymous profiles older than 90 days without conversion"
unengaged
"Suppress users with no email opens in 180+ days"
console
log
"=== Customer.io Cost Audit Rules ===\n"
for
const
of
Object
entries
AUDIT_RULES
console
log
`${rule}: ${action}`
console
log
"\nRun these checks in Customer.io dashboard:"
console
log
"1. People > Segments > Create 'Inactive 90 days' segment"
console
log
"2. People > Segments > Create 'No email attribute' segment"
console
log
"3. People > Filter by created_at < 90 days ago AND email_opened = 0"
Step 2: Suppress and Delete Inactive Users
import { TrackClient, RegionUS } from "customerio-node";
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
interface CleanupTarget {
userId: string;
reason: string;
}
async function cleanupInactiveUsers(
targets: CleanupTarget[],
dryRun: boolean = true
): Promise<void> {
let suppressed = 0;
let deleted = 0;
let errors = 0;
for (const target of targets) {
if (dryRun) {
console.log(`[DRY RUN] Would suppress+delete: ${target.userId} (${target.reason})`);
continue;
}
try {
await cio.suppress(target.userId);
suppressed++;
await cio.destroy(target.userId);
deleted++;
await new Promise((r) => setTimeout(r, 20));
} catch (err: any) {
errors++;
console.error(`Failed ${target.userId}: ${err.message}`);
}
if ((suppressed + errors) % 100 === 0) {
console.log(`Progress: ${suppressed} deleted, ${errors} errors`);
}
}
console.log(`\nResult: ${suppressed} suppressed, ${deleted} deleted, ${errors} errors`);
}
Step 3: Event Deduplication
import { createHash } from "crypto";
import { TrackClient, RegionUS } from "customerio-node";
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
const recentEvents = new Map<string, number>();
const MAX_CACHE = 50_000;
const DEDUP_WINDOW_MS = 60 * 1000;
function isDuplicate(userId: string, eventName: string, data?: any): boolean {
const hash = createHash("sha256")
.update(`${userId}:${eventName}:${JSON.stringify(data ?? {})}`)
.digest("hex")
.substring(0, 12);
const last = recentEvents.get(hash);
if (last && Date.now() - last < DEDUP_WINDOW_MS) {
return true;
}
recentEvents.set(hash, Date.now());
if (recentEvents.size > MAX_CACHE) {
const cutoff = Date.now() - DEDUP_WINDOW_MS;
for (const [key, time] of recentEvents) {
if (time < cutoff) recentEvents.delete(key);
}
}
return false;
}
export async function trackDeduped(
userId: string,
name: string,
data?: Record<string, any>
): Promise<void> {
if (isDuplicate(userId, name, data)) {
return;
}
await cio.track(userId, { name, data });
}
Step 4: Event Sampling for High-Volume Events
const EVENT_SAMPLE_RATES: Record<string, number> = {
page_viewed: 0.1,
button_clicked: 0.25,
search_performed: 0.5,
signed_up: 1.0,
checkout_completed: 1.0,
subscription_cancelled: 1.0,
};
export function shouldTrack(eventName: string): boolean {
const rate = EVENT_SAMPLE_RATES[eventName] ?? 1.0;
return Math.random() < rate;
}
if (shouldTrack("page_viewed")) {
await cio.track(userId, {
name: "page_viewed",
data: { url: "/pricing", sampled: true },
});
}
Step 5: Usage Monitoring
interface UsageMetrics {
identifyCalls: number;
trackCalls: number;
transactionalSends: number;
broadcastTriggers: number;
webhooksReceived: number;
}
class UsageMonitor {
private metrics: UsageMetrics = {
identifyCalls: 0,
trackCalls: 0,
transactionalSends: 0,
broadcastTriggers: 0,
webhooksReceived: 0,
};
increment(metric: keyof UsageMetrics): void {
this.metrics[metric]++;
}
report(): void {
console.log("\n=== Customer.io Usage Report ===");
console.log(`Period: ${new Date().toISOString()}`);
for (const [key, value] of Object.entries(this.metrics)) {
console.log(` ${key}: ${value.toLocaleString()}`);
}
const total = Object.values(this.metrics).reduce((a, b) => a + b, 0);
console.log(` TOTAL API calls: ${total.toLocaleString()}`);
}
reset(): void {
for (const key of Object.keys(this.metrics)) {
this.metrics[key as keyof UsageMetrics] = 0;
}
}
}
export const usageMonitor = new UsageMonitor();
Cost Savings Estimates
| Optimization | Typical Savings | Implementation Effort |
|---|
| Delete inactive profiles (180+ days) | 15-30% profile cost | Low |
| Event deduplication | 5-15% event volume | Low |
| Event sampling (analytics events) | 50-80% event volume for sampled events | Low |
| Suppress bounced emails | 2-5% email cost | Low |
| Don't identify email-less users | 5-20% profile cost | Medium |
| Annual billing | 10-20% total cost | None |
Monthly Cost Review Checklist
Resources
Next Steps
After cost optimization, proceed to customerio-reference-architecture for enterprise patterns.