From bee026f68b8ebeecc666e9034524b9867e7078f6 Mon Sep 17 00:00:00 2001 From: chrizzellu Date: Tue, 18 Aug 2026 11:46:28 -0400 Subject: [PATCH] fix(klaviyo): send revenue value on the event attributes A mapping rule's `settings.value` was assigned to `properties.value`, where Klaviyo treats it as a segmentable custom property and ignores it for revenue reporting, while `valueCurrency` was emitted on the event attributes with no sibling `value` to denominate. `klaviyo-api`'s ObjectSerializer builds the request body from EventCreateQueryV2ResourceObjectAttributes.attributeTypeMap, which maps `value` -> `value` and `valueCurrency` -> `value_currency` at the attributes level; `properties` is typed `object` and passes through untouched. So the currency was reaching Klaviyo and the amount never was. Both now sit together on the attributes. Properties are unaffected -- map a value through the rule's `data` mapping to also keep it as a custom property. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/klaviyo-revenue-value-attribute.md | 11 +++++++++++ .../server/destinations/klaviyo/src/examples/step.ts | 3 +-- packages/server/destinations/klaviyo/src/push.ts | 12 ++++++++++-- .../destinations/klaviyo/src/schemas/mapping.ts | 2 +- .../server/destinations/klaviyo/src/types/index.ts | 2 +- website/docs/destinations/server/klaviyo.mdx | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 .changeset/klaviyo-revenue-value-attribute.md diff --git a/.changeset/klaviyo-revenue-value-attribute.md b/.changeset/klaviyo-revenue-value-attribute.md new file mode 100644 index 000000000..c269c7698 --- /dev/null +++ b/.changeset/klaviyo-revenue-value-attribute.md @@ -0,0 +1,11 @@ +--- +'@walkeros/server-destination-klaviyo': patch +--- + +Revenue now reaches Klaviyo. A mapping rule's `settings.value` was written into +the event's `properties`, where Klaviyo stores it as a segmentable custom +property and ignores it for revenue reporting, while `valueCurrency` was set on +the event attributes with no sibling value to denominate. Both now sit together +on the attributes, which is where `klaviyo-api` serializes `value` and +`value_currency` from. Properties are unaffected -- map a value through the +rule's `data` mapping if you also want it as a custom property. diff --git a/packages/server/destinations/klaviyo/src/examples/step.ts b/packages/server/destinations/klaviyo/src/examples/step.ts index 7cef8899e..992f1918b 100644 --- a/packages/server/destinations/klaviyo/src/examples/step.ts +++ b/packages/server/destinations/klaviyo/src/examples/step.ts @@ -154,7 +154,6 @@ export const revenueEvent: KlaviyoStepExample = { data: { map: { OrderId: 'data.id', - value: 'data.total', ItemNames: 'data.itemNames', }, }, @@ -186,10 +185,10 @@ export const revenueEvent: KlaviyoStepExample = { }, properties: { OrderId: 'ORD-123', - value: 99.99, ItemNames: ['Widget A', 'Widget B'], }, time: new Date(1700000102).toISOString(), + value: 99.99, valueCurrency: 'EUR', }, }, diff --git a/packages/server/destinations/klaviyo/src/push.ts b/packages/server/destinations/klaviyo/src/push.ts index 06eeca524..15fe3e96a 100644 --- a/packages/server/destinations/klaviyo/src/push.ts +++ b/packages/server/destinations/klaviyo/src/push.ts @@ -84,7 +84,14 @@ export const push: PushFn = async function ( ? { ...(data as Record) } : {}; - // Handle revenue value + // Handle revenue value. + // + // Klaviyo reads revenue from the event's `value` / `valueCurrency` + // attributes (serialized by klaviyo-api as `value` / `value_currency`), + // NOT from `properties`. A number nested in `properties` is stored as a + // segmentable custom property and is ignored by revenue reporting, so + // emitting `valueCurrency` without a sibling `value` denominates nothing. + let value: number | undefined; let valueCurrency: string | undefined; if (mappingSettings.value !== undefined) { const resolvedValue = await getMappingValue( @@ -96,7 +103,7 @@ export const push: PushFn = async function ( ); const numericValue = toNumber(resolvedValue); if (numericValue !== undefined) { - properties.value = numericValue; + value = numericValue; if (settings.currency) { valueCurrency = settings.currency; } @@ -121,6 +128,7 @@ export const push: PushFn = async function ( }, properties, time: timestamp.toISOString(), + ...(value !== undefined ? { value } : {}), ...(valueCurrency ? { valueCurrency } : {}), }, }, diff --git a/packages/server/destinations/klaviyo/src/schemas/mapping.ts b/packages/server/destinations/klaviyo/src/schemas/mapping.ts index cdb6d383f..be22c8a9c 100644 --- a/packages/server/destinations/klaviyo/src/schemas/mapping.ts +++ b/packages/server/destinations/klaviyo/src/schemas/mapping.ts @@ -10,7 +10,7 @@ export const MappingSchema = z.object({ value: z .unknown() .describe( - 'Revenue value mapping. Resolves to a numeric value for Klaviyo revenue tracking. Sets the value property and valueCurrency on the event.', + 'Revenue value mapping. Resolves to a numeric value for Klaviyo revenue tracking. Sets the event value attribute (value on the wire), plus valueCurrency when settings.currency is set.', ) .optional(), }); diff --git a/packages/server/destinations/klaviyo/src/types/index.ts b/packages/server/destinations/klaviyo/src/types/index.ts index afc1fe161..9dc1f10c7 100644 --- a/packages/server/destinations/klaviyo/src/types/index.ts +++ b/packages/server/destinations/klaviyo/src/types/index.ts @@ -40,7 +40,7 @@ export type InitSettings = Partial; export interface Mapping { /** Per-event identify mapping. Resolves to profile attributes for upsert. */ identify?: WalkerOSMapping.Value; - /** Revenue value mapping. Resolves to numeric value for Klaviyo's $value. */ + /** Revenue value mapping. Resolves to the event's numeric `value` attribute. */ value?: WalkerOSMapping.Value; } diff --git a/website/docs/destinations/server/klaviyo.mdx b/website/docs/destinations/server/klaviyo.mdx index d05db80c4..3a28ea6ff 100644 --- a/website/docs/destinations/server/klaviyo.mdx +++ b/website/docs/destinations/server/klaviyo.mdx @@ -83,7 +83,7 @@ Identity is resolved automatically from each event: `email` defaults to `user.em ## Revenue tracking -Map a mapping rule's `settings.value` to a numeric event property. When `settings.currency` is also set, the destination adds `valueCurrency` on the Klaviyo event for revenue reporting. +Map a mapping rule's `settings.value` to a numeric value. The destination sets it as the Klaviyo event's `value` attribute -- the field revenue reporting reads -- and when `settings.currency` is also set, adds `valueCurrency` alongside it. ## Ecommerce metric naming