customerio-observability
Guide for implementing observability, metrics, and alerting for Customer.io service integrations.
Install
mkdir -p .claude/skills/customerio-observability && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4118" && unzip -o skill.zip -d .claude/skills/customerio-observability && rm skill.zipInstalls to .claude/skills/customerio-observability
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Set up Customer.io monitoring and observability.Key capabilities
- →Track Customer.io API call duration and request counts.
- →Monitor Customer.io API error rates by operation and status code.
- →Count transactional and campaign emails sent.
- →Track email bounces and spam complaints from webhooks.
- →Monitor pending items in the Customer.io event queue.
How it works
This skill instruments Customer.io API calls and webhook events to collect metrics and logs, which are then exposed via Prometheus and visualized in Grafana. It also defines alerting rules based on these metrics.
Inputs & outputs
When to use customerio-observability
- →Setting up Grafana dashboards for API performance
- →Tracking email delivery metrics
- →Implementing structured logging with PII redaction
- →Configuring alerts for API error rates
About this skill
Customer.io Observability
Overview
Implement comprehensive observability for Customer.io integrations: Prometheus metrics (latency, error rates, delivery funnel), structured JSON logging with PII redaction, OpenTelemetry tracing, and Grafana dashboard definitions.
Prerequisites
- Customer.io integration deployed
- Prometheus + Grafana (or compatible metrics stack)
- Structured logging system (pino recommended)
Key Metrics to Track
| Metric | Type | Description | Alert Threshold |
|---|---|---|---|
cio_api_duration_ms | Histogram | API call latency | p99 > 5000ms |
cio_api_requests_total | Counter | Total API requests by operation | N/A (rate) |
cio_api_errors_total | Counter | API errors by status code | > 1% error rate |
cio_email_sent_total | Counter | Transactional + campaign emails | N/A |
cio_email_bounced_total | Counter | Bounce count | > 5% of sends |
cio_email_complained_total | Counter | Spam complaints | > 0.1% of sends |
cio_webhook_received_total | Counter | Webhook events by metric type | N/A |
cio_queue_depth | Gauge | Pending items in event queue | > 10K |
Instructions
Step 1: Prometheus Metrics
// lib/customerio-metrics.ts
import { Counter, Histogram, Gauge, Registry } from "prom-client";
const registry = new Registry();
export const cioMetrics = {
apiDuration: new Histogram({
name: "cio_api_duration_ms",
help: "Customer.io API call duration in milliseconds",
labelNames: ["operation", "status"] as const,
buckets: [10, 25, 50, 100, 250, 500, 1000, 2500, 5000],
registers: [registry],
}),
apiRequests: new Counter({
name: "cio_api_requests_total",
help: "Total Customer.io API requests",
labelNames: ["operation"] as const,
registers: [registry],
}),
apiErrors: new Counter({
name: "cio_api_errors_total",
help: "Customer.io API errors",
labelNames: ["operation", "status_code"] as const,
registers: [registry],
}),
emailSent: new Counter({
name: "cio_email_sent_total",
help: "Emails sent via Customer.io",
labelNames: ["type"] as const, // "transactional" or "campaign"
registers: [registry],
}),
emailBounced: new Counter({
name: "cio_email_bounced_total",
help: "Email bounces from Customer.io webhooks",
registers: [registry],
}),
emailComplained: new Counter({
name: "cio_email_complained_total",
help: "Spam complaints from Customer.io webhooks",
registers: [registry],
}),
webhookReceived: new Counter({
name: "cio_webhook_received_total",
help: "Webhook events received",
labelNames: ["metric"] as const,
registers: [registry],
}),
queueDepth: new Gauge({
name: "cio_queue_depth",
help: "Pending items in Customer.io event queue",
labelNames: ["queue"] as const,
registers: [registry],
}),
};
export { registry };
Step 2: Instrumented Client
// lib/customerio-instrumented.ts
import { TrackClient, APIClient, SendEmailRequest, RegionUS } from "customerio-node";
import { cioMetrics } from "./customerio-metrics";
export class InstrumentedCioClient {
private track: TrackClient;
private app: APIClient;
constructor(siteId: string, trackKey: string, appKey: string) {
this.track = new TrackClient(siteId, trackKey, { region: RegionUS });
this.app = new APIClient(appKey, { region: RegionUS });
}
async identify(userId: string, attrs: Record<string, any>): Promise<void> {
const timer = cioMetrics.apiDuration.startTimer({ operation: "identify" });
cioMetrics.apiRequests.inc({ operation: "identify" });
try {
await this.track.identify(userId, attrs);
timer({ status: "success" });
} catch (err: any) {
const code = String(err.statusCode ?? "unknown");
timer({ status: "error" });
cioMetrics.apiErrors.inc({ operation: "identify", status_code: code });
throw err;
}
}
async trackEvent(
userId: string,
name: string,
data?: Record<string, any>
): Promise<void> {
const timer = cioMetrics.apiDuration.startTimer({ operation: "track" });
cioMetrics.apiRequests.inc({ operation: "track" });
try {
await this.track.track(userId, { name, data });
timer({ status: "success" });
} catch (err: any) {
timer({ status: "error" });
cioMetrics.apiErrors.inc({
operation: "track",
status_code: String(err.statusCode ?? "unknown"),
});
throw err;
}
}
async sendEmail(request: SendEmailRequest): Promise<any> {
const timer = cioMetrics.apiDuration.startTimer({ operation: "send_email" });
cioMetrics.apiRequests.inc({ operation: "send_email" });
try {
const result = await this.app.sendEmail(request);
timer({ status: "success" });
cioMetrics.emailSent.inc({ type: "transactional" });
return result;
} catch (err: any) {
timer({ status: "error" });
cioMetrics.apiErrors.inc({
operation: "send_email",
status_code: String(err.statusCode ?? "unknown"),
});
throw err;
}
}
}
Step 3: Structured Logging with PII Redaction
// lib/customerio-logger.ts
import pino from "pino";
const logger = pino({
name: "customerio",
level: process.env.CUSTOMERIO_LOG_LEVEL ?? "info",
redact: {
paths: [
"*.email",
"*.phone",
"*.ip_address",
"*.password",
"attrs.email",
"attrs.phone",
],
censor: "[REDACTED]",
},
});
export function logCioOperation(
operation: string,
data: {
userId?: string;
event?: string;
latencyMs?: number;
statusCode?: number;
error?: string;
attrs?: Record<string, any>;
}
): void {
if (data.error) {
logger.error({ operation, ...data }, `CIO ${operation} failed`);
} else {
logger.info({ operation, ...data }, `CIO ${operation} completed`);
}
}
// Usage:
// logCioOperation("identify", {
// userId: "user-123",
// latencyMs: 85,
// attrs: { email: "[email protected]", plan: "pro" }
// });
// Output: {"level":"info","operation":"identify","userId":"user-123",
// "latencyMs":85,"attrs":{"email":"[REDACTED]","plan":"pro"},
// "msg":"CIO identify completed"}
Step 4: Webhook Metrics Collection
// Integrate with webhook handler (see customerio-webhooks-events skill)
function recordWebhookMetrics(event: { metric: string }): void {
cioMetrics.webhookReceived.inc({ metric: event.metric });
switch (event.metric) {
case "bounced":
cioMetrics.emailBounced.inc();
break;
case "spammed":
cioMetrics.emailComplained.inc();
break;
case "sent":
cioMetrics.emailSent.inc({ type: "campaign" });
break;
}
}
Step 5: Prometheus Metrics Endpoint
// routes/metrics.ts
import { Router } from "express";
import { registry } from "../lib/customerio-metrics";
const router = Router();
router.get("/metrics", async (_req, res) => {
res.set("Content-Type", registry.contentType);
res.end(await registry.metrics());
});
export default router;
Step 6: Grafana Dashboard (JSON Model)
{
"title": "Customer.io Integration",
"panels": [
{
"title": "API Latency (p50/p95/p99)",
"type": "timeseries",
"targets": [
{ "expr": "histogram_quantile(0.50, rate(cio_api_duration_ms_bucket[5m]))" },
{ "expr": "histogram_quantile(0.95, rate(cio_api_duration_ms_bucket[5m]))" },
{ "expr": "histogram_quantile(0.99, rate(cio_api_duration_ms_bucket[5m]))" }
]
},
{
"title": "Request Rate by Operation",
"type": "timeseries",
"targets": [
{ "expr": "rate(cio_api_requests_total[5m])" }
]
},
{
"title": "Error Rate %",
"type": "stat",
"targets": [
{ "expr": "rate(cio_api_errors_total[5m]) / rate(cio_api_requests_total[5m]) * 100" }
]
},
{
"title": "Email Delivery Funnel",
"type": "bargauge",
"targets": [
{ "expr": "cio_email_sent_total" },
{ "expr": "cio_email_bounced_total" },
{ "expr": "cio_email_complained_total" }
]
}
]
}
Step 7: Alerting Rules
# prometheus/customerio-alerts.yml
groups:
- name: customerio
rules:
- alert: CioHighErrorRate
expr: rate(cio_api_errors_total[5m]) / rate(cio_api_requests_total[5m]) > 0.05
for: 5m
labels: { severity: critical }
annotations:
summary: "Customer.io API error rate > 5%"
- alert: CioHighLatency
expr: histogram_quantile(0.99, rate(cio_api_duration_ms_bucket[5m])) > 5000
for: 5m
labels: { severity: warning }
annotations:
summary: "Customer.io p99 latency > 5 seconds"
- alert: CioHighBounceRate
expr: rate(cio_email_bounced_total[1h]) / rate(cio_email_sent_total[1h]) > 0.05
for: 15m
labels: { severity: warning }
annotations:
summary: "Email bounce rate > 5%"
- alert: CioSpamComplaints
expr: rate(cio_email_complained_total[1h]) / rate(cio_email_sent_total[1h]) > 0.001
for: 5m
labels: { severity: critical }
annotations:
summary: "Spam complaint rate > 0.1% — sender reputation at risk"
Error Handling
| Issue | Solution |
|---|---|
| High cardinality metrics | Don't use userId as a label — use operation + status only |
| Log volume too high | Set CUSTOMERIO_LOG_LEVEL=warn in production |
| Missing metrics | Check metric registration and scrape config |
| PII in logs | Verify pino redact paths cover all sensitive fields |
Resources
Next Steps
After observability se
Content truncated.
When not to use it
- →When user IDs are used as metric labels, leading to high cardinality.
- →When log volume is too high for production environments.
- →When PII is not redacted from logs.
Prerequisites
Limitations
- →Requires careful management of metric cardinality to avoid performance issues.
- →Requires explicit configuration of log levels to manage volume.
- →Requires verification of PII redaction paths for sensitive fields.
How it compares
This skill provides a structured, automated approach to monitoring Customer.io integrations with predefined metrics and alerts, unlike manual inspection of logs or API responses.
Compared to similar skills
customerio-observability side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| customerio-observability (this skill) | 1 | 27d | Review | Advanced |
| distributed-tracing | 5 | 2mo | No flags | Intermediate |
| service-mesh-observability | 5 | 2mo | No flags | Advanced |
| observability-engineer | 12 | 4mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
distributed-tracing
wshobson
Implement distributed tracing with Jaeger and Tempo to track requests across microservices and identify performance bottlenecks. Use when debugging microservices, analyzing request flows, or implementing observability for distributed systems.
service-mesh-observability
wshobson
Implement comprehensive observability for service meshes including distributed tracing, metrics, and visualization. Use when setting up mesh monitoring, debugging latency issues, or implementing SLOs for service communication.
observability-engineer
sickn33
Build production-ready monitoring, logging, and tracing systems. Implements comprehensive observability strategies, SLI/SLO management, and incident response workflows. Use PROACTIVELY for monitoring infrastructure, performance optimization, or production reliability.
prometheus-configuration
wshobson
Set up Prometheus for comprehensive metric collection, storage, and monitoring of infrastructure and applications. Use when implementing metrics collection, setting up monitoring infrastructure, or configuring alerting systems.
langfuse
davila7
Expert in Langfuse - the open-source LLM observability platform. Covers tracing, prompt management, evaluation, datasets, and integration with LangChain, LlamaIndex, and OpenAI. Essential for debugging, monitoring, and improving LLM applications in production. Use when: langfuse, llm observability, llm tracing, prompt management, llm evaluation.
slo-implementation
wshobson
Define and implement Service Level Indicators (SLIs) and Service Level Objectives (SLOs) with error budgets and alerting. Use when establishing reliability targets, implementing SRE practices, or measuring service performance.