Claude-code-plugins-plus-skills klaviyo-hello-world

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-hello-world" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-klaviyo-hello-world && rm -rf "$T"
manifest: plugins/saas-packs/klaviyo-pack/skills/klaviyo-hello-world/SKILL.md
source content

Klaviyo Hello World

Overview

Minimal working example: create a profile, track an event, and query the result using the

klaviyo-api
Node.js SDK against
a.klaviyo.com/api/*
.

Prerequisites

  • Completed
    klaviyo-install-auth
    setup
  • KLAVIYO_PRIVATE_KEY
    set in environment
  • klaviyo-api
    package installed

Instructions

Step 1: Create a Profile

// hello-klaviyo.ts
import {
  ApiKeySession,
  ProfilesApi,
  EventsApi,
  ProfileCreateQuery,
  ProfileEnum,
  EventCreateQueryV2,
  EventEnum,
} from 'klaviyo-api';

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

// Create or update a profile
// NOTE: SDK uses camelCase (firstName, not first_name)
const profilePayload: ProfileCreateQuery = {
  data: {
    type: ProfileEnum.Profile,
    attributes: {
      email: 'hello@example.com',
      firstName: 'Hello',
      lastName: 'World',
      properties: {
        source: 'hello-world-script',
        signupDate: new Date().toISOString(),
      },
    },
  },
};

const profile = await profilesApi.createProfile(profilePayload);
console.log('Profile created:', profile.body.data.id);

Step 2: Track an Event

// Track a custom event tied to the profile
const eventPayload: EventCreateQueryV2 = {
  data: {
    type: EventEnum.Event,
    attributes: {
      // The metric name -- creates the metric if it doesn't exist
      metric: {
        data: {
          type: 'metric',
          attributes: {
            name: 'Hello World Test',
          },
        },
      },
      // Link to the profile by email
      profile: {
        data: {
          type: ProfileEnum.Profile,
          attributes: {
            email: 'hello@example.com',
          },
        },
      },
      properties: {
        message: 'First event from API!',
        timestamp: new Date().toISOString(),
      },
      time: new Date().toISOString(),
      value: 0,
    },
  },
};

await eventsApi.createEvent(eventPayload);
console.log('Event tracked: Hello World Test');

Step 3: Retrieve the Profile

// Fetch profiles filtered by email
const profiles = await profilesApi.getProfiles({
  filter: 'equals(email,"hello@example.com")',
});

const p = profiles.body.data[0];
console.log(`Found: ${p.attributes.firstName} ${p.attributes.lastName}`);
console.log(`ID: ${p.id}`);
console.log(`Created: ${p.attributes.created}`);

Step 4: Complete Script

// hello-klaviyo.ts -- full runnable script
import {
  ApiKeySession,
  ProfilesApi,
  EventsApi,
  ProfileEnum,
} from 'klaviyo-api';

async function main() {
  const session = new ApiKeySession(process.env.KLAVIYO_PRIVATE_KEY!);
  const profilesApi = new ProfilesApi(session);
  const eventsApi = new EventsApi(session);

  // 1. Create profile
  const profile = await profilesApi.createProfile({
    data: {
      type: ProfileEnum.Profile,
      attributes: {
        email: 'hello@example.com',
        firstName: 'Hello',
        lastName: 'World',
      },
    },
  });
  console.log(`Profile created: ${profile.body.data.id}`);

  // 2. Track event
  await eventsApi.createEvent({
    data: {
      type: 'event',
      attributes: {
        metric: { data: { type: 'metric', attributes: { name: 'Hello World Test' } } },
        profile: { data: { type: 'profile', attributes: { email: 'hello@example.com' } } },
        properties: { source: 'hello-world' },
        time: new Date().toISOString(),
      },
    },
  });
  console.log('Event tracked successfully');

  // 3. Query profile back
  const result = await profilesApi.getProfiles({
    filter: 'equals(email,"hello@example.com")',
  });
  console.log(`Verified: ${result.body.data[0]?.attributes.firstName}`);
}

main().catch(console.error);

Run it:

npx tsx hello-klaviyo.ts

Output

Profile created: 01JXXXXXXXXXXXXXXXXXXXXXX
Event tracked successfully
Verified: Hello

Error Handling

ErrorStatusCauseSolution
Duplicate profile
409Email already existsUse
createOrUpdateProfile
instead
Invalid email format
400Malformed emailValidate email before sending
Missing metric name
400Empty metric objectAlways include
metric.data.attributes.name
Unauthorized
401Bad API keyCheck
KLAVIYO_PRIVATE_KEY
env var

Key SDK Conventions

  • camelCase properties: The SDK uses
    firstName
    ,
    phoneNumber
    ,
    lastName
    (not snake_case)
  • JSON:API format: All payloads use
    { data: { type, attributes } }
    structure
  • Response body: Access via
    response.body.data
    (not
    response.data
    )
  • Profile identifiers: Use
    email
    ,
    phoneNumber
    , or
    externalId
    to identify profiles

Resources

Next Steps

Proceed to

klaviyo-local-dev-loop
for development workflow setup, or
klaviyo-core-workflow-a
for profile and list management.