Agent Skills: Klaviyo Security Basics

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/klaviyo-security-basics

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/klaviyo-pack/skills/klaviyo-security-basics

Skill Files

Browse the full folder contents for klaviyo-security-basics.

Download Skill

Loading file tree…

plugins/saas-packs/klaviyo-pack/skills/klaviyo-security-basics/SKILL.md

Skill Metadata

Name
klaviyo-security-basics
Description
|

Klaviyo Security Basics

Overview

Security best practices for Klaviyo: API key types, OAuth scopes, webhook HMAC-SHA256 signature verification, and secret rotation procedures.

Prerequisites

  • Klaviyo account with API key access
  • Understanding of environment variables and secret management
  • Access to Klaviyo dashboard (Settings > API Keys)

Instructions

Step 1: Understand Key Types

| Key Type | Format | Use Case | Sensitivity | |----------|--------|----------|-------------| | Private API Key | pk_* (40+ chars) | Server-side REST API | CRITICAL -- never expose client-side | | Public API Key | 6 alphanumeric chars | Client-side Track/Identify only | Low -- safe in browser JS |

Private keys authenticate via Authorization: Klaviyo-API-Key pk_*** header. Public keys pass as company_id query parameter.

Step 2: Environment Variable Configuration

# .env (NEVER commit)
KLAVIYO_PRIVATE_KEY=pk_***************************************
KLAVIYO_PUBLIC_KEY=UXxxXx
KLAVIYO_WEBHOOK_SIGNING_SECRET=whsec_*************************

# .gitignore -- mandatory entries
.env
.env.local
.env.*.local
// src/config/klaviyo.ts -- validated config loader
function requireEnv(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Missing required env: ${name}`);
  return value;
}

export const klaviyoConfig = {
  privateKey: requireEnv('KLAVIYO_PRIVATE_KEY'),
  publicKey: process.env.KLAVIYO_PUBLIC_KEY || '',
  webhookSecret: process.env.KLAVIYO_WEBHOOK_SIGNING_SECRET || '',
};

Step 3: Least-Privilege API Key Scopes

Create separate API keys per environment with minimal scopes:

| Environment | Recommended Scopes | Rationale | |-------------|-------------------|-----------| | Development | profiles:read, events:read, lists:read | Read-only exploration | | Staging | profiles:read/write, events:write, lists:read/write | Full test coverage | | Production | Exact scopes your app needs | Minimize blast radius | | CI/CD | profiles:read, events:read | Smoke tests only |

# Use separate env vars per environment
KLAVIYO_PRIVATE_KEY_DEV=pk_dev_***
KLAVIYO_PRIVATE_KEY_STAGING=pk_staging_***
KLAVIYO_PRIVATE_KEY_PROD=pk_prod_***

Step 4: Webhook Signature Verification (HMAC-SHA256)

Klaviyo signs webhook payloads using HMAC-SHA256 with your webhook signing secret.

// src/klaviyo/webhook-verify.ts
import crypto from 'crypto';

/**
 * Verify Klaviyo webhook signature.
 * Klaviyo uses the webhook signing secret (set when creating the webhook)
 * to compute an HMAC-SHA256 signature of the payload.
 */
export function verifyKlaviyoWebhookSignature(
  payload: Buffer | string,
  signature: string,
  secret: string
): boolean {
  if (!signature || !secret) return false;

  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(typeof payload === 'string' ? payload : payload.toString())
    .digest('base64');

  // Timing-safe comparison to prevent timing attacks
  try {
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expectedSignature)
    );
  } catch {
    return false;  // Different lengths
  }
}

Step 5: Express Webhook Middleware

import express from 'express';

app.post('/webhooks/klaviyo',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['klaviyo-webhook-signature'] as string;

    if (!verifyKlaviyoWebhookSignature(
      req.body,
      signature,
      process.env.KLAVIYO_WEBHOOK_SIGNING_SECRET!
    )) {
      console.warn('[Security] Invalid webhook signature rejected');
      return res.status(401).json({ error: 'Invalid signature' });
    }

    const event = JSON.parse(req.body.toString());
    // Process verified event...
    res.status(200).json({ received: true });
  }
);

Step 6: API Key Rotation Procedure

# 1. Generate new key in Klaviyo dashboard (Settings > API Keys)
#    - Name it with date: "Production API Key 2025-03"
#    - Assign same scopes as the old key

# 2. Deploy new key (zero-downtime)
#    Update secret in your deployment platform:
#    - Vercel: vercel env add KLAVIYO_PRIVATE_KEY production
#    - AWS: aws secretsmanager update-secret --secret-id klaviyo-key --secret-string pk_new_***
#    - GCP: echo -n "pk_new_***" | gcloud secrets versions add klaviyo-key --data-file=-

# 3. Verify new key works
curl -s -w "%{http_code}" -o /dev/null \
  -H "Authorization: Klaviyo-API-Key pk_new_***" \
  -H "revision: 2024-10-15" \
  "https://a.klaviyo.com/api/accounts/"

# 4. Revoke old key in Klaviyo dashboard
#    Settings > API Keys > Delete old key

# 5. Audit: check logs for any 401s after rotation

Security Checklist

  • [ ] Private API keys stored in environment variables / secret manager
  • [ ] .env files in .gitignore
  • [ ] Different API keys per environment (dev/staging/prod)
  • [ ] Minimal scopes per environment
  • [ ] Webhook signatures verified with HMAC-SHA256
  • [ ] API key rotation scheduled (quarterly recommended)
  • [ ] No private keys in client-side code
  • [ ] CI/CD uses read-only key for tests
  • [ ] Git history scanned for leaked keys (git log -p | grep pk_)

Error Handling

| Security Issue | Detection | Mitigation | |----------------|-----------|------------| | Leaked private key | Git scanning, trufflehog | Revoke immediately, rotate | | Excessive scopes | Scope audit | Reduce to minimum required | | Missing webhook verification | Code review | Add HMAC check | | Key not rotated | Age > 90 days | Schedule rotation | | 401s after rotation | Log monitoring | Verify all services updated |

Resources

Next Steps

For production deployment, see klaviyo-prod-checklist.