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