Skip to main content
customerio-reliability-patterns Implement Customer.io reliability and fault-tolerance patterns.
Use when building circuit breakers, fallback queues, idempotency,
or graceful degradation for Customer.io integrations.
Trigger: "customer.io reliability", "customer.io resilience",
"customer.io circuit breaker", "customer.io fault tolerance".
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill customerio-reliability-patternsThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... Related occupations SOC
Based on SOC occupation classification
More from this repository Implement user sign-up and sign-in flows with Clerk.
Use when building authentication UI, customizing sign-in experience,
or implementing OAuth social login.
Trigger with phrases like "clerk sign-in", "clerk sign-up",
"clerk login flow", "clerk OAuth", "clerk social login".
Implement session management and middleware with Clerk.
Use when managing user sessions, configuring route protection,
or implementing token refresh and custom JWT templates.
Trigger with phrases like "clerk session", "clerk middleware",
"clerk route protection", "clerk token", "clerk JWT".
Configure enterprise SSO, role-based access control, and organization management.
Use when implementing SSO integration, configuring role-based permissions,
or setting up organization-level controls.
Trigger with phrases like "clerk SSO", "clerk RBAC",
"clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
name customerio-reliability-patterns description Implement Customer.io reliability and fault-tolerance patterns.
Use when building circuit breakers, fallback queues, idempotency,
or graceful degradation for Customer.io integrations.
Trigger: "customer.io reliability", "customer.io resilience",
"customer.io circuit breaker", "customer.io fault tolerance".
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","reliability","circuit-breaker","resilience"] compatibility Designed for Claude Code, also compatible with Codex and OpenClaw
Customer.io Reliability Patterns
Overview
Implement fault-tolerant Customer.io integrations: circuit breaker (stop cascading failures), retry with jitter (handle transient errors), fallback queue (survive outages), idempotency guard (prevent duplicates), and graceful degradation (never crash your app for analytics).
Prerequisites
Working Customer.io integration
Understanding of failure modes (429, 5xx, timeouts, DNS failures)
Redis (recommended for queue-based patterns)
Instructions
Pattern 1: Circuit Breaker
type CircuitState = "CLOSED" | "OPEN" | "HALF_OPEN" ;
export class CircuitBreaker {
private state : CircuitState = "CLOSED" ;
private failureCount = 0 ;
private successCount = 0 ;
private lastFailureTime = 0 ;
constructor (
private readonly failureThreshold : number = 5 ,
private readonly successThreshold : number = 3 ,
private readonly resetTimeoutMs : number = 30000
) {}
get currentState (): CircuitState {
if (this . === ) {
( . () - . > . ) {
. = ;
. = ;
}
}
. ;
}
execute<T>( : <T>): <T> {
( . === ) {
( );
}
{
result = ();
. ();
result;
} (err) {
. ();
err;
}
}
(): {
. = ;
( . === ) {
. ++;
( . >= . ) {
. = ;
. ( );
}
}
}
(): {
. ++;
. = . ();
( . >= . ) {
. = ;
. (
+
);
}
}
(): { : ; : ; : | } {
{
: . ,
: . ,
: . ? ( . ) : ,
};
}
}
state
"OPEN"
if
Date
now
this
lastFailureTime
this
resetTimeoutMs
this
state
"HALF_OPEN"
this
successCount
0
return
this
state
async
fn
() =>
Promise
Promise
if
this
currentState
"OPEN"
throw
new
Error
"Circuit breaker is OPEN — Customer.io calls blocked"
try
const
await
fn
this
onSuccess
return
catch
this
onFailure
throw
private
onSuccess
void
this
failureCount
0
if
this
state
"HALF_OPEN"
this
successCount
if
this
successCount
this
successThreshold
this
state
"CLOSED"
console
log
"Circuit breaker: CLOSED (recovered)"
private
onFailure
void
this
failureCount
this
lastFailureTime
Date
now
if
this
failureCount
this
failureThreshold
this
state
"OPEN"
console
warn
`Circuit breaker: OPEN (${this .failureCount} failures). `
`Will retry in ${this .resetTimeoutMs / 1000 } s`
getStatus
state
CircuitState
failures
number
lastFailure
Date
null
return
state
this
currentState
failures
this
failureCount
lastFailure
this
lastFailureTime
new
Date
this
lastFailureTime
null
Pattern 2: Retry with Jitter
export async function retryWithJitter<T>(
fn : () => Promise <T>,
maxRetries = 3 ,
baseDelayMs = 1000
): Promise <T> {
for (let attempt = 0 ; attempt <= maxRetries; attempt++) {
try {
return await fn ();
} catch (err : any ) {
const status = err.statusCode ?? err.status ;
if (status >= 400 && status < 500 && status !== 429 ) throw err;
if (attempt === maxRetries) throw err;
const delay = baseDelayMs * Math .pow (2 , attempt);
const jitter = delay * 0.3 * Math .random ();
await new Promise ((r ) => setTimeout (r, delay + jitter));
}
}
throw new Error ("Unreachable" );
}
Pattern 3: Fallback Queue
import { Queue , Worker } from "bullmq" ;
import { TrackClient , RegionUS } from "customerio-node" ;
const REDIS_URL = process.env .REDIS_URL ?? "redis://localhost:6379" ;
const fallbackQueue = new Queue ("cio:fallback" , {
connection : { url : REDIS_URL },
defaultJobOptions : {
attempts : 10 ,
backoff : { type : "exponential" , delay : 60000 },
removeOnComplete : 1000 ,
removeOnFail : 5000 ,
},
});
export async function enqueueFallback (
operation : "identify" | "track" | "suppress" ,
data : Record <string , any >
): Promise <void > {
await fallbackQueue.add (operation, data);
console .log (`CIO fallback: queued ${operation} (circuit open)` );
}
export function startFallbackWorker ( ): void {
const cio = new TrackClient (
process.env .CUSTOMERIO_SITE_ID !,
process.env .CUSTOMERIO_TRACK_API_KEY !,
{ region : RegionUS }
);
new Worker ("cio:fallback" , async (job) => {
switch (job.name ) {
case "identify" :
await cio.identify (job.data .userId , job.data .attrs );
break ;
case "track" :
await cio.track (job.data .userId , job.data .event );
break ;
case "suppress" :
await cio.suppress (job.data .userId );
break ;
}
}, {
connection : { url : REDIS_URL },
concurrency : 5 ,
});
}
Pattern 4: Resilient Client (All Patterns Combined)
import { TrackClient , RegionUS } from "customerio-node" ;
import { CircuitBreaker } from "./circuit-breaker" ;
import { retryWithJitter } from "./retry" ;
import { enqueueFallback } from "./customerio-fallback" ;
export class ResilientCioClient {
private client : TrackClient ;
private breaker : CircuitBreaker ;
constructor (siteId : string , apiKey : string ) {
this .client = new TrackClient (siteId, apiKey, { region : RegionUS });
this .breaker = new CircuitBreaker (5 , 3 , 30000 );
}
async identify (userId : string , attrs : Record <string , any >): Promise <void > {
try {
await this .breaker .execute (() =>
retryWithJitter (() => this .client .identify (userId, attrs))
);
} catch (err : any ) {
if (err.message .includes ("Circuit breaker is OPEN" )) {
await enqueueFallback ("identify" , { userId, attrs });
return ;
}
console .error (`CIO identify failed for ${userId} : ${err.message} ` );
}
}
async track (
userId : string ,
name : string ,
data ?: Record <string , any >
): Promise <void > {
try {
await this .breaker .execute (() =>
retryWithJitter (() =>
this .client .track (userId, { name, data })
)
);
} catch (err : any ) {
if (err.message .includes ("Circuit breaker is OPEN" )) {
await enqueueFallback ("track" , { userId, event : { name, data } });
return ;
}
console .error (`CIO track failed for ${userId} /${name} : ${err.message} ` );
}
}
getHealthStatus ( ) {
return this .breaker .getStatus ();
}
}
Pattern 5: Idempotency Guard
import { createHash } from "crypto" ;
const processedOps = new Map <string , number >();
const MAX_ENTRIES = 100_000 ;
const TTL_MS = 5 * 60 * 1000 ;
export function isIdempotent (
operation : string ,
userId : string ,
data : any
): boolean {
const hash = createHash ("sha256" )
.update (`${operation} :${userId} :${JSON .stringify(data)} ` )
.digest ("hex" )
.substring (0 , 16 );
const existing = processedOps.get (hash);
if (existing && Date .now () - existing < TTL_MS ) {
return true ;
}
processedOps.set (hash, Date .now ());
if (processedOps.size > MAX_ENTRIES ) {
const cutoff = Date .now () - TTL_MS ;
for (const [key, time] of processedOps) {
if (time < cutoff) processedOps.delete (key);
}
}
return false ;
}
Pattern 6: Health Check Endpoint
import { ResilientCioClient } from "../lib/customerio-resilient" ;
const cio = new ResilientCioClient (
process.env .CUSTOMERIO_SITE_ID !,
process.env .CUSTOMERIO_TRACK_API_KEY !
);
app.get ("/health/customerio" , (_req, res ) => {
const status = cio.getHealthStatus ();
const healthy = status.state === "CLOSED" ;
res.status (healthy ? 200 : 503 ).json ({
customerio : {
circuit_state : status.state ,
failure_count : status.failures ,
last_failure : status.lastFailure ?.toISOString () ?? null ,
},
});
});
Pattern Selection Guide Scenario Pattern Priority Transient 5xx errors Retry with jitter Must have Extended Customer.io outage Circuit breaker + fallback queue Must have Duplicate events from retries Idempotency guard Should have App must never crash for tracking Graceful degradation (catch all) Must have Need visibility into reliability Health check endpoint Should have
Reliability Checklist
Resources
Next Steps After reliability patterns, proceed to customerio-load-scale for load testing and scaling.