Claude-code-plugins klaviyo-ci-integration

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/klaviyo-pack/skills/klaviyo-ci-integration" ~/.claude/skills/jeremylongshore-claude-code-plugins-klaviyo-ci-integration && rm -rf "$T"
manifest: plugins/saas-packs/klaviyo-pack/skills/klaviyo-ci-integration/SKILL.md
source content

Klaviyo CI Integration

Overview

Set up GitHub Actions CI/CD pipelines for Klaviyo integrations with unit tests, integration tests against the real API, and deployment automation.

Prerequisites

  • GitHub repository with Actions enabled
  • Klaviyo test API key (from a test/sandbox account)
  • klaviyo-api
    SDK and vitest configured

Instructions

Step 1: Configure GitHub Secrets

# Store Klaviyo test credentials as GitHub secrets
gh secret set KLAVIYO_PRIVATE_KEY --body "pk_test_***"
gh secret set KLAVIYO_WEBHOOK_SIGNING_SECRET --body "whsec_test_***"

Step 2: CI Workflow

Create

.github/workflows/klaviyo-ci.yml
:

name: Klaviyo Integration CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npx tsc --noEmit
      - run: npm test -- --coverage
      - name: Check Klaviyo SDK version
        run: npm list klaviyo-api

  integration-tests:
    runs-on: ubuntu-latest
    needs: unit-tests
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    env:
      KLAVIYO_PRIVATE_KEY: ${{ secrets.KLAVIYO_PRIVATE_KEY }}
      KLAVIYO_TEST: '1'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - name: Run integration tests
        run: npm run test:integration
        timeout-minutes: 5
      - name: Verify Klaviyo connectivity
        run: |
          curl -s -w "HTTP %{http_code}\n" -o /dev/null \
            -H "Authorization: Klaviyo-API-Key $KLAVIYO_PRIVATE_KEY" \
            -H "revision: 2024-10-15" \
            "https://a.klaviyo.com/api/accounts/"

Step 3: Unit Test Examples

// tests/unit/klaviyo-events.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ApiKeySession, EventsApi, ProfileEnum } from 'klaviyo-api';

vi.mock('klaviyo-api', () => ({
  ApiKeySession: vi.fn(),
  EventsApi: vi.fn().mockImplementation(() => ({
    createEvent: vi.fn().mockResolvedValue({ body: { data: { id: 'EVT_MOCK' } } }),
    getEvents: vi.fn().mockResolvedValue({
      body: { data: [], links: { next: null } },
    }),
  })),
  ProfileEnum: { Profile: 'profile' },
  EventEnum: { Event: 'event' },
}));

describe('Event Tracking', () => {
  let eventsApi: EventsApi;

  beforeEach(() => {
    eventsApi = new EventsApi(new ApiKeySession('pk_test'));
  });

  it('tracks a purchase event', async () => {
    const result = await eventsApi.createEvent({
      data: {
        type: 'event' as any,
        attributes: {
          metric: { data: { type: 'metric', attributes: { name: 'Placed Order' } } },
          profile: { data: { type: 'profile', attributes: { email: 'test@example.com' } } },
          properties: { orderId: 'ORD-TEST-001', value: 99.99 },
          time: new Date().toISOString(),
        },
      },
    });
    expect(result.body.data.id).toBe('EVT_MOCK');
  });
});

Step 4: Integration Test (Gated)

// tests/integration/klaviyo-live.test.ts
import { describe, it, expect } from 'vitest';
import { ApiKeySession, AccountsApi, ProfilesApi, ProfileEnum } from 'klaviyo-api';

const SKIP = !process.env.KLAVIYO_TEST || !process.env.KLAVIYO_PRIVATE_KEY;

describe.skipIf(SKIP)('Klaviyo Live API', () => {
  const session = new ApiKeySession(process.env.KLAVIYO_PRIVATE_KEY!);

  it('authenticates successfully', async () => {
    const accountsApi = new AccountsApi(session);
    const accounts = await accountsApi.getAccounts();
    expect(accounts.body.data).toHaveLength(1);
  });

  it('creates and cleans up a test profile', async () => {
    const profilesApi = new ProfilesApi(session);
    const testEmail = `ci-test-${Date.now()}@example.com`;

    const created = await profilesApi.createProfile({
      data: {
        type: ProfileEnum.Profile,
        attributes: {
          email: testEmail,
          firstName: 'CI',
          lastName: 'Test',
          properties: { source: 'github-actions', timestamp: new Date().toISOString() },
        },
      },
    });

    expect(created.body.data.id).toBeTruthy();
    expect(created.body.data.attributes.email).toBe(testEmail);
  });
});

Step 5: PR Checks Configuration

# .github/branch-protection.yml (or set via GitHub UI)
# Required checks: unit-tests
# Integration tests: optional (only on main push)

Output

  • Unit tests run on every PR (mocked, no API key needed)
  • Integration tests run on main branch pushes (real API)
  • SDK version verified in CI
  • API connectivity smoke test included

Error Handling

IssueCauseSolution
Secret not found in CIMissing
gh secret set
Add secret via repository settings
Integration test 429Rate limited in CIAdd delays between tests, use dedicated test key
Auth failures in CIWrong secret nameVerify secret name matches workflow env var
Test timeoutSlow Klaviyo responseIncrease
timeout-minutes

Resources

Next Steps

For deployment patterns, see

klaviyo-deploy-integration
.