Complete checklist for deploying Klaviyo integrations to production, with health checks, rollback procedures, and validation against real Klaviyo API endpoints.
pk_*)KLAVIYO_PRIVATE_KEY stored in secret manager (not env file)KLAVIYO_WEBHOOK_SIGNING_SECRET) configuredKLAVIYO_PUBLIC_KEY) set for client-side tracking (if used)grep -r "pk_" src/)klaviyo-api SDK (not raw HTTP)package.json (not ^ or *)revision header set to 2024-10-15 (or current supported revision)createOrUpdateProfile (upsert, not create)uniqueId for deduplication where applicable+15551234567)Retry-After header// src/health/klaviyo.ts
import { ApiKeySession, AccountsApi } from 'klaviyo-api';
export async function checkKlaviyoHealth(): Promise<{
status: 'healthy' | 'degraded' | 'down';
latencyMs: number;
accountId?: string;
error?: string;
}> {
const start = Date.now();
try {
const session = new ApiKeySession(process.env.KLAVIYO_PRIVATE_KEY!);
const accountsApi = new AccountsApi(session);
const result = await accountsApi.getAccounts();
return {
status: 'healthy',
latencyMs: Date.now() - start,
accountId: result.body.data[0].id,
};
} catch (error: any) {
return {
status: error.status === 429 ? 'degraded' : 'down',
latencyMs: Date.now() - start,
error: `${error.status}: ${error.body?.errors?.[0]?.detail || error.message}`,
};
}
}
// Express health endpoint
app.get('/health', async (req, res) => {
const klaviyo = await checkKlaviyoHealth();
const overallStatus = klaviyo.status === 'healthy' ? 200 : 503;
res.status(overallStatus).json({
status: klaviyo.status,
services: { klaviyo },
timestamp: new Date().toISOString(),
});
});
#!/bin/bash
# scripts/preflight-klaviyo.sh
set -euo pipefail
echo "=== Klaviyo Production Pre-Flight ==="
# 1. Check Klaviyo status
echo -n "Klaviyo Status Page: "
STATUS=$(curl -s "https://status.klaviyo.com/api/v2/status.json" | python3 -c "import sys,json; print(json.load(sys.stdin)['status']['description'])" 2>/dev/null)
echo "$STATUS"
[ "$STATUS" = "All Systems Operational" ] || echo "WARNING: Klaviyo has active incidents"
# 2. Verify API key
echo -n "API Auth: "
HTTP_CODE=$(curl -s -w "%{http_code}" -o /dev/null \
-H "Authorization: Klaviyo-API-Key $KLAVIYO_PRIVATE_KEY" \
-H "revision: 2024-10-15" \
"https://a.klaviyo.com/api/accounts/")
echo "HTTP $HTTP_CODE"
[ "$HTTP_CODE" = "200" ] || { echo "FAIL: API auth returned $HTTP_CODE"; exit 1; }
# 3. Check rate limit headroom
echo -n "Rate Limit: "
curl -s -I \
-H "Authorization: Klaviyo-API-Key $KLAVIYO_PRIVATE_KEY" \
-H "revision: 2024-10-15" \
"https://a.klaviyo.com/api/profiles/?page[size]=1" 2>/dev/null \
| grep -i "ratelimit-remaining" || echo "Headers not available"
# 4. Verify SDK version
echo -n "SDK Version: "
node -e "console.log(require('klaviyo-api/package.json').version)" 2>/dev/null || echo "Not installed"
echo ""
echo "=== Pre-flight complete ==="
# Immediate rollback: disable Klaviyo integration
# Option 1: Feature flag (preferred)
# Set KLAVIYO_ENABLED=false in your deployment platform
# Option 2: Deploy previous version
git log --oneline -5 # Find last known-good commit
git revert HEAD # Revert the deployment commit
# Push and deploy
# Option 3: If using Kubernetes
kubectl rollout undo deployment/your-app
kubectl rollout status deployment/your-app
| Alert | Condition | Severity |
|---|---|---|
| API Auth Failure | Any 401/403 | P1 -- key may be revoked |
| API Unreachable | 5xx > 10/min | P1 -- check status page |
| Rate Limited | 429 > 5/min | P2 -- reduce request volume |
| High Latency | P95 > 5s | P2 -- check network/Klaviyo load |
| Webhook Signature Invalid | Any rejection | P2 -- verify signing secret |
For version upgrades, see klaviyo-upgrade-migration.