From klaviyo-pack
Diagnose and fix common Klaviyo API errors like 400 bad request, 401 unauthorized, 403 forbidden using real payloads, causes, and TypeScript SDK fixes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/klaviyo-pack:klaviyo-common-errorsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Quick reference for the most common Klaviyo API errors with real error payloads, root causes, and solutions.
Quick reference for the most common Klaviyo API errors with real error payloads, root causes, and solutions.
klaviyo-api SDK installedKlaviyo returns JSON:API error responses. Extract the status code and error detail:
try {
await profilesApi.createProfile(payload);
} catch (error: any) {
console.error('Status:', error.status);
console.error('Errors:', JSON.stringify(error.body?.errors, null, 2));
// error.body.errors[] has: { id, code, title, detail, source }
}
Actual Klaviyo response:
{
"errors": [{
"id": "abc-123",
"code": "invalid",
"title": "Invalid input.",
"detail": "The email field is required.",
"source": { "pointer": "/data/attributes/email" }
}]
}
Common causes:
+15551234567)type value in JSON:API payloadsnake_case instead of camelCase (SDK uses camelCase)Fix:
// Wrong: snake_case
{ first_name: 'Jane', phone_number: '+155...' }
// Right: camelCase (SDK convention)
{ firstName: 'Jane', phoneNumber: '+15551234567' }
Actual response:
{
"errors": [{
"code": "not_authenticated",
"title": "Authentication credentials were not provided.",
"detail": "Missing or invalid Authorization header."
}]
}
Root causes:
KLAVIYO_PRIVATE_KEY environment variablepk_*)Fix:
# Verify key is set and starts with pk_
echo $KLAVIYO_PRIVATE_KEY | head -c 3
# Should print: pk_
# Test with cURL
curl -s -w "%{http_code}" -o /dev/null \
-H "Authorization: Klaviyo-API-Key $KLAVIYO_PRIVATE_KEY" \
-H "revision: 2024-10-15" \
"https://a.klaviyo.com/api/accounts/"
Actual response:
{
"errors": [{
"code": "permission_denied",
"title": "You do not have permission to perform this action.",
"detail": "The API key does not have the required scope: profiles:write"
}]
}
Fix: Generate a new API key with the required scope at Settings > API Keys > Create Private API Key.
| Endpoint | Required Scope |
|---|---|
POST /api/profiles/ | profiles:write |
GET /api/segments/ | segments:read |
POST /api/events/ | events:write |
POST /api/campaigns/ | campaigns:write |
POST /api/data-privacy-deletion-jobs/ | data-privacy:write |
Typical causes:
/api/v2/ is dead, use /api/)Fix:
// Verify the resource exists first
const lists = await listsApi.getLists();
const targetList = lists.body.data.find(l => l.attributes.name === 'Newsletter');
if (!targetList) throw new Error('List not found');
Actual response:
{
"errors": [{
"code": "duplicate",
"title": "Conflict.",
"detail": "A profile already exists with the email [email protected]"
}]
}
Fix: Use createOrUpdateProfile (upsert) instead of createProfile:
// This handles both create and update
await profilesApi.createOrUpdateProfile({
data: {
type: 'profile' as any,
attributes: { email: '[email protected]', firstName: 'Updated' },
},
});
Headers on 429 response:
Retry-After: 10
Klaviyo rate limits (per-account, fixed window):
| Window | Limit |
|---|---|
| Burst (1 second) | 75 requests |
| Steady (1 minute) | 700 requests |
Note: When rate limited, RateLimit-Remaining and RateLimit-Reset headers are NOT returned. Only Retry-After (integer seconds) is present.
Fix: Honor Retry-After header:
catch (error: any) {
if (error.status === 429) {
const retryAfter = parseInt(error.headers?.['retry-after'] || '10');
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
// Retry the request
}
}
Fix:
klaviyo-rate-limits)| Error | Cause | Fix |
|---|---|---|
Cannot find module 'klaviyo-api' | Wrong package | npm install klaviyo-api (not @klaviyo/sdk) |
TypeError: ... is not a constructor | Wrong import | Use new ProfilesApi(session) not new KlaviyoClient() |
response.data is undefined | Wrong access pattern | Use response.body.data (not response.data) |
filter is not valid | Bad filter syntax | Use equals(field,"value") not field = value |
# Check Klaviyo API health
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Klaviyo-API-Key $KLAVIYO_PRIVATE_KEY" \
-H "revision: 2024-10-15" \
"https://a.klaviyo.com/api/accounts/"
# Check Klaviyo status page
curl -s https://status.klaviyo.com/api/v2/status.json | python3 -m json.tool
# Verify local env
env | grep KLAVIYO
npm list klaviyo-api
klaviyo-debug-bundleFor comprehensive debugging, see klaviyo-debug-bundle.
5plugins reuse this skill
First indexed Jul 10, 2026
npx claudepluginhub fleet-to-force/claude-code-plugins-plus --plugin klaviyo-packDiagnoses and fixes common Klaviyo API errors (4xx/5xx) with real payloads, root causes, and remedies. Use when encountering Klaviyo API failures or SDK integration issues.
Integrates Klaviyo email/SMS marketing API for customer profiles, event tracking, campaigns, flows, and segmentation. Includes setup examples for Node.js and Python.
Guides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.