Skip to content
Back to skills

Klaviyo Core Workflow A

ASecurity

'Execute Klaviyo primary workflow: profiles, lists, and subscriptions.

  • 2,688 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 29, 2026
ai-agentstypescriptbashapi

Works with

  • claude code
  • api

Security analysis

A100/100

Scanned May 29, 2026

npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill klaviyo-core-workflow-a --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Klaviyo Core Workflow A?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Klaviyo Core Workflow A
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jeremylongshore-klaviyo-core-workflow-a/badge)](https://www.skillsdirectory.com/skills/jeremylongshore-klaviyo-core-workflow-a)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: klaviyo-core-workflow-a
description: 'Execute Klaviyo primary workflow: profiles, lists, and subscriptions.

  Use when creating/updating profiles, managing lists, subscribing contacts,

  or syncing customer data to Klaviyo for email/SMS marketing.

  Trigger with phrases like "klaviyo profiles", "klaviyo lists",

  "klaviyo subscribe", "add contacts to klaviyo", "klaviyo customer data".

  '
allowed-tools: Read, Write, Edit, Bash(npm:*), Grep
version: 1.0.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- klaviyo
- email-marketing
- cdp
compatibility: Designed for Claude Code
---
# Klaviyo Core Workflow A -- Profiles, Lists & Subscriptions

## Overview

Primary money-path workflow: create/update profiles, manage lists, and subscribe contacts for email and SMS marketing via the `klaviyo-api` SDK.

## Prerequisites

- Completed `klaviyo-install-auth` setup
- API key with scopes: `profiles:read`, `profiles:write`, `lists:read`, `lists:write`

## Instructions

### Step 1: Create or Update a Profile

```typescript
import { ApiKeySession, ProfilesApi, ProfileEnum } from 'klaviyo-api';

const session = new ApiKeySession(process.env.KLAVIYO_PRIVATE_KEY!);
const profilesApi = new ProfilesApi(session);

// Create a new profile (409 if email already exists)
const newProfile = await profilesApi.createProfile({
  data: {
    type: ProfileEnum.Profile,
    attributes: {
      email: 'customer@example.com',
      firstName: 'Jane',
      lastName: 'Doe',
      phoneNumber: '+15551234567',
      location: {
        city: 'Atlanta',
        region: 'GA',
        country: 'US',
        zip: '30309',
      },
      properties: {
        plan: 'pro',
        signupSource: 'website',
        lifetime_value: 250.00,
      },
    },
  },
});
console.log('Profile ID:', newProfile.body.data.id);

// Create OR update (upsert) -- preferred for syncing
const upserted = await profilesApi.createOrUpdateProfile({
  data: {
    type: ProfileEnum.Profile,
    attributes: {
      email: 'customer@example.com',
      firstName: 'Jane',
      lastName: 'Doe-Smith',  // Updated last name
      properties: {
        plan: 'enterprise',   // Updated plan
        lastLogin: new Date().toISOString(),
      },
    },
  },
});
console.log('Upserted profile:', upserted.body.data.id);
```

### Step 2: Create a List

```typescript
import { ListsApi, ListEnum } from 'klaviyo-api';

const listsApi = new ListsApi(session);

// Create a new list
const list = await listsApi.createList({
  data: {
    type: ListEnum.List,
    attributes: {
      name: 'Newsletter Subscribers',
    },
  },
});
const listId = list.body.data.id;
console.log('List created:', listId);

// Get all lists
const allLists = await listsApi.getLists();
for (const l of allLists.body.data) {
  console.log(`${l.attributes.name} (${l.id})`);
}
```

### Step 3: Add Profiles to a List

```typescript
// Add existing profiles to a list (does NOT change subscription status)
await listsApi.createListRelationships({
  id: listId,
  relationshipType: 'profiles' as any,
  body: {
    data: [
      { type: ProfileEnum.Profile, id: 'PROFILE_ID_1' },
      { type: ProfileEnum.Profile, id: 'PROFILE_ID_2' },
    ],
  },
});
```

### Step 4: Subscribe Profiles (Email + SMS Consent)

```typescript
// Subscribe profiles to a list WITH marketing consent
// This is the correct way to add subscribers (not just list members)
await profilesApi.subscribeProfiles({
  data: {
    type: 'profile-subscription-bulk-create-job',
    attributes: {
      profiles: {
        data: [
          {
            type: ProfileEnum.Profile,
            attributes: {
              email: 'subscriber@example.com',
              phoneNumber: '+15559876543',
              subscriptions: {
                email: {
                  marketing: {
                    consent: 'SUBSCRIBED',
                    consentTimestamp: new Date().toISOString(),
                  },
                },
                sms: {
                  marketing: {
                    consent: 'SUBSCRIBED',
                    consentTimestamp: new Date().toISOString(),
                  },
                },
              },
            },
          },
        ],
      },
    },
    relationships: {
      list: {
        data: {
          type: ListEnum.List,
          id: listId,
        },
      },
    },
  },
});
console.log('Profile subscribed to email + SMS');
```

### Step 5: Query Profiles with Filters

```typescript
// Filter profiles by custom property
const proUsers = await profilesApi.getProfiles({
  filter: 'equals(properties.plan,"pro")',
  sort: '-created',  // Newest first
});

// Filter by date range
const recentProfiles = await profilesApi.getProfiles({
  filter: 'greater-than(created,2024-01-01T00:00:00Z)',
});

// Filter by email domain
const gmailUsers = await profilesApi.getProfiles({
  filter: 'contains(email,"@gmail.com")',
});

// Get profiles for a specific list
const listMembers = await listsApi.getListProfiles({ id: listId });
for (const member of listMembers.body.data) {
  console.log(member.attributes.email);
}
```

### Step 6: Bulk Profile Import

```typescript
// Batch create/update up to 100 profiles at a time
const profiles = customers.map(c => ({
  type: ProfileEnum.Profile as const,
  attributes: {
    email: c.email,
    firstName: c.firstName,
    lastName: c.lastName,
    properties: { source: 'bulk-import', importedAt: new Date().toISOString() },
  },
}));

// Process in batches of 100
for (let i = 0; i < profiles.length; i += 100) {
  const batch = profiles.slice(i, i + 100);
  await Promise.all(
    batch.map(p => profilesApi.createOrUpdateProfile({ data: p }))
  );
  console.log(`Imported ${Math.min(i + 100, profiles.length)}/${profiles.length}`);
}
```

## Output

- Profiles created/updated in Klaviyo
- Lists created and populated
- Subscribers opted in with consent timestamps
- Queryable customer data for segmentation

## Error Handling

| Error | Status | Cause | Solution |
|-------|--------|-------|----------|
| Duplicate profile | 409 | Email exists | Use `createOrUpdateProfile` (upsert) |
| Invalid phone | 400 | Wrong format | Use E.164 format: `+15551234567` |
| List not found | 404 | Wrong list ID | Verify list ID via `getLists()` |
| Missing consent | 400 | No consent timestamp | Always include `consentTimestamp` |
| Rate limited | 429 | >75 req/s burst | See `klaviyo-rate-limits` |

## Resources

- [Profiles API](https://developers.klaviyo.com/en/reference/profiles_api_overview)
- [Lists API](https://developers.klaviyo.com/en/reference/lists_api_overview)
- [Subscribe Profiles](https://developers.klaviyo.com/en/reference/bulk_subscribe_profiles)
- [Consent Collection Guide](https://developers.klaviyo.com/en/docs/collect_email_and_sms_consent_via_api)

## Next Steps

For event tracking and campaign triggers, see `klaviyo-core-workflow-b`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.