Agent Skills: Klaviyo Core Workflow A -- Profiles, Lists & Subscriptions

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/klaviyo-core-workflow-a

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-core-workflow-a

Skill Files

Browse the full folder contents for klaviyo-core-workflow-a.

Download Skill

Loading file tree…

plugins/saas-packs/klaviyo-pack/skills/klaviyo-core-workflow-a/SKILL.md

Skill Metadata

Name
klaviyo-core-workflow-a
Description
|

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

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

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

// 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)

// 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

// 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

// 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

Next Steps

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