Skip to content

feat(klaviyo): Upgrade API to 2026-01-15 with feature flag#3707

Draft
harsh-joshi99 wants to merge 4 commits intomainfrom
klaviyo-api-2026-01-15
Draft

feat(klaviyo): Upgrade API to 2026-01-15 with feature flag#3707
harsh-joshi99 wants to merge 4 commits intomainfrom
klaviyo-api-2026-01-15

Conversation

@harsh-joshi99
Copy link
Copy Markdown
Contributor

@harsh-joshi99 harsh-joshi99 commented Mar 31, 2026

Summary

Upgrades Klaviyo API from 2025-01-15 to 2026-01-15 behind feature flag `klaviyo-canary-version`, validated against the real Klaviyo API.

Changes

Version Management

  • ✅ Created `versioning-info.ts` with `KLAVIYO_API_REVISION` / `KLAVIYO_CANARY_API_REVISION`
  • ✅ `getApiRevision(features)` helper + `FLAGON_NAME = 'klaviyo-canary-version'`
  • ✅ `extendRequest` passes `features` → revision-aware `revision` header
  • ✅ Flag naming consistent with `google-enhanced-canary-version`, `first-party-dv360-canary-version`, etc.

Real API Validation

  • ✅ Added `validation/` harness — makes real HTTP calls to both revisions
  • 9/9 endpoints structurally identical across `2025-01-15` and `2026-01-15`
  • ✅ `validation-report.md` committed as evidence (delete at canary promotion)
  • Fixtures cover: `POST /profiles/`, `POST /profile-bulk-import-jobs/` (×2), `GET /lists/`, `GET /profiles/`, `POST /events/`, `POST /event-bulk-create-jobs/`, subscribe, unsubscribe

Unit Tests

  • ✅ 135/135 tests passing
  • ✅ Feature flag tests verify correct `revision` header for stable and canary paths

Skill

  • ✅ `api-version-upgrade` skill added to `.claude/skills/`
  • ✅ Step 5.5 added to skill: run validation harness before committing
  • ✅ Flag naming convention, import-order warning, and test command fixes

Known Limitation

`buildHeaders()` calls in `getList`, `createList`, `createAudience`, `getAudience` use stable revision unconditionally — these audience-management functions don't have `features` in their call signatures. Follow-up PR if needed.

Breaking Changes

Risk Level: LOW — All changes between `2025-01-15` and `2026-01-15` are additive. Confirmed by real API validation. See `breaking-changes-analysis.md`.

Validation Report

See `packages/destination-actions/src/destinations/klaviyo/validation/validation-report.md`

✅ All 9 endpoints are structurally identical across both revisions. Safe to promote canary.

Rollout Plan

  1. Phase 1: Merge, flag `klaviyo-canary-version` off by default
  2. Phase 2: Enable for internal testing
  3. Phase 3: Gradual rollout
  4. Phase 4: Full rollout, promote canary → stable
  5. Phase 5: Remove flag, delete `validation-report.md` and `breaking-changes-analysis.md`

🤖 Generated with Claude Code

- Create versioning-info.ts with KLAVIYO_API_REVISION (2025-01-15) and KLAVIYO_CANARY_API_REVISION (2026-01-15)
- Add FLAGON_NAME constant 'klaviyo-canary-api-revision' and getApiRevision(features) helper in functions.ts
- Update buildHeaders() to accept optional features parameter for feature-flagged revision header
- Update extendRequest in index.ts to pass features to buildHeaders
- Add feature flag tests verifying stable and canary revision headers
- All 135 tests passing

Breaking changes analysis: no breaking changes in 2025-01-15 → 2026-01-15 window

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the api-version-upgrade Claude Code skill to .claude/skills/ for
shared use across the team. Includes the main SKILL.md workflow, reference
docs for common patterns/feature flags/testing, and evals.

Updated test command to use `npx jest` directly (fixes --testPathPattern
deprecation in newer Jest versions).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades the Klaviyo destination’s API revision handling to support a 2026-01-15 canary revision behind a feature flag, while keeping 2025-01-15 as the default stable revision. It also adds internal documentation (“skill” guides) describing a standardized workflow for API version upgrades with canary flags and testing.

Changes:

  • Added stable/canary revision constants and a getApiRevision(features) helper, then updated request header construction to be feature-flag aware.
  • Updated Klaviyo destination request extension to pass features so the revision header can be toggled per-request.
  • Added unit tests validating stable vs canary revision behavior, plus new docs/skill references for version-upgrade workflows.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/destination-actions/src/destinations/klaviyo/versioning-info.ts Introduces stable/canary Klaviyo API revision constants.
packages/destination-actions/src/destinations/klaviyo/functions.ts Adds feature-flag helper and updates buildHeaders to select revision dynamically.
packages/destination-actions/src/destinations/klaviyo/index.ts Threads features into extendRequest so header revision can be toggled.
packages/destination-actions/src/destinations/klaviyo/config.ts Sources REVISION_DATE from the new versioning constants.
packages/destination-actions/src/destinations/klaviyo/breaking-changes-analysis.md Documents changelog review for 2025-01-15 → 2026-01-15.
packages/destination-actions/src/destinations/klaviyo/tests/index.test.ts Adds tests for default vs feature-flagged revision header behavior.
.claude/skills/api-version-upgrade/SKILL.md Adds a procedural guide for performing canary-based API upgrades.
.claude/skills/api-version-upgrade/references/feature-flags.md Adds reference guidance for feature flags (naming, examples, testing).
.claude/skills/api-version-upgrade/references/testing-guide.md Adds reference guidance for running/structuring tests during upgrades.
.claude/skills/api-version-upgrade/references/common-patterns.md Adds reference catalog of versioning patterns used across destinations.
.claude/skills/api-version-upgrade/evals/evals.json Adds eval scenarios for validating the upgrade workflow.

Comment on lines +138 to +142
export function buildHeaders(authKey: string, features?: Features) {
return {
Authorization: `Klaviyo-API-Key ${authKey}`,
Accept: 'application/json',
revision: REVISION_DATE,
revision: getApiRevision(features),
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildHeaders now supports feature-flagged revisions, but several call sites still invoke it without passing features (e.g., within this file and in klaviyo/index.ts audience methods). Because request-client merges headers with the per-request headers taking precedence, those requests will always use the stable revision even when the canary flag is enabled. Thread features through those helper calls (or avoid overriding headers so extendRequest can supply them) to ensure the revision is consistent across all requests.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to 66
extendRequest({ settings, features }) {
return {
headers: buildHeaders(settings.api_key)
headers: buildHeaders(settings.api_key, features)
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extendRequest is feature-flag aware, but audienceConfig.createAudience/getAudience override headers via headers: buildHeaders(apiKey) and don’t pass createAudienceInput.features/getAudienceInput.features. Since per-request headers override the extended headers, canary users will still send the stable revision for these audience calls. Pass the features from the input into buildHeaders (or rely on extendRequest headers) so the feature flag applies consistently.

Copilot uses AI. Check for mistakes.
Comment on lines +174 to +177
// Pattern: {DESTINATION}_API_VERSION
export const KLAVIYO_API_VERSION = '2025-01-15'
export const KLAVIYO_CANARY_API_VERSION = '2026-01-15'

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant-name examples use KLAVIYO_API_VERSION / KLAVIYO_CANARY_API_VERSION, but this PR introduces KLAVIYO_API_REVISION / KLAVIYO_CANARY_API_REVISION for Klaviyo. Update the examples (or generalize them) so the guide doesn’t point contributors to non-existent identifiers.

Suggested change
// Pattern: {DESTINATION}_API_VERSION
export const KLAVIYO_API_VERSION = '2025-01-15'
export const KLAVIYO_CANARY_API_VERSION = '2026-01-15'
// Pattern: {DESTINATION}_API_REVISION (for date-based APIs like Klaviyo)
export const KLAVIYO_API_REVISION = '2025-01-15'
export const KLAVIYO_CANARY_API_REVISION = '2026-01-15'
// Pattern: {DESTINATION}_API_VERSION (for versioned APIs like Google CM360)

Copilot uses AI. Check for mistakes.
harsh-joshi99 and others added 2 commits March 31, 2026 21:19
…ntion

- Move FLAGON_NAME/getApiRevision exports after all import statements
  (fixes import/first lint violation flagged in code review)
- Rename feature flag from 'klaviyo-canary-api-revision' to
  'klaviyo-canary-version' to match repo convention used by
  google-enhanced, first-party-dv360, facebook-capi, etc.
- Update skill docs to use consistent '-canary-version' suffix
  with real in-repo examples, add import-order warning to SKILL.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a __validation__/ suite that makes real HTTP calls against both
the stable (2025-01-15) and canary (2026-01-15) revisions and
structurally diffs the responses — catching breaking changes that
mocked unit tests cannot detect.

Files:
- fixtures.ts  — one fixture per API endpoint; write fixtures use
                 revision-scoped emails to prevent 409 conflicts
- normalizer.ts — strips non-deterministic fields (IDs, timestamps)
                  before diffing so structural changes are signal
- differ.ts    — deep structural diff with human-readable output
- validate.ts  — runner script, sequential calls per fixture, writes
                 validation-report.md

Result: 9/9 endpoints structurally identical across both revisions.
validation-report.md committed as evidence.

Also:
- gitignore __validation__/validation-report.md by default; force-add
  to upgrade PRs only, delete at canary promotion cleanup
- Add Step 5.5 to api-version-upgrade skill documenting the workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 31, 2026 21:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

@@ -0,0 +1,238 @@
#!/usr/bin/env npx ts-node
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shebang #!/usr/bin/env npx ts-node is not a valid/portable shebang on most systems because the kernel only passes a single optional argument to the interpreter; /usr/bin/env will try to execute a literal command named "npx ts-node" and fail if someone runs this script directly. Consider removing the shebang entirely (since the documented usage is npx ts-node ...) or switching to a single-command shebang like #!/usr/bin/env ts-node (and ensure ts-node is resolvable).

Suggested change
#!/usr/bin/env npx ts-node

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +88
resolve({
status: res.statusCode ?? 0,
headers: res.headers as Record<string, string>,
body: parsed
})
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

res.headers is IncomingHttpHeaders (string | string[] | undefined values). Casting it to Record<string, string> can produce arrays/undefined at runtime and may cause the normalizer/differ to report false diffs or throw in edge cases. Prefer normalizing headers into a string map (e.g., join string arrays and drop undefined) before returning HttpResponse.headers.

Copilot uses AI. Check for mistakes.
}

if (Array.isArray(stable) !== Array.isArray(canary)) {
changes.push({ type: 'changed', path, stable: 'array', canary: typeof canary })
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the array-vs-non-array mismatch branch, the Change details are incorrect when the canary value is the array (it always sets stable: 'array' and canary: typeof canary, which would be 'object' for arrays). To keep the report accurate, compute both sides (e.g., stable: Array.isArray(stable) ? 'array' : typeof stable, canary: Array.isArray(canary) ? 'array' : typeof canary).

Suggested change
changes.push({ type: 'changed', path, stable: 'array', canary: typeof canary })
changes.push({
type: 'changed',
path,
stable: Array.isArray(stable) ? 'array' : typeof stable,
canary: Array.isArray(canary) ? 'array' : typeof canary
})

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants