Skills Development Klaviyo CI/CD Integration With GitHub Actions

Klaviyo CI/CD Integration With GitHub Actions

v20260423
klaviyo-ci-integration
A comprehensive guide to setting up CI/CD pipelines for Klaviyo integrations using GitHub Actions. This guide covers configuring GitHub secrets, running unit tests, and executing real-world integration tests against the live Klaviyo API, ensuring reliable deployment automation for your marketing SDKs.
Get Skill
285 downloads
Overview

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

Issue Cause Solution
Secret not found in CI Missing gh secret set Add secret via repository settings
Integration test 429 Rate limited in CI Add delays between tests, use dedicated test key
Auth failures in CI Wrong secret name Verify secret name matches workflow env var
Test timeout Slow Klaviyo response Increase timeout-minutes

Resources

Next Steps

For deployment patterns, see klaviyo-deploy-integration.

Info
Category Development
Name klaviyo-ci-integration
Version v20260423
Size 6.02KB
Updated At 2026-04-28
Language