email

Klaviyo Flow Revenue vs Campaign Revenue: The Real Attribution Model

Klaviyo reports Flow Revenue and Campaign Revenue separately. Most dashboards double-count them or conflate them with Shopify revenue. A real methodology for separating them in a single attribution model.

By Admaxxer Team • May 17, 2026 • 11 min read

Klaviyo's reporting separates revenue into two distinct columns — Flow Revenue and Campaign Revenue — and most dashboards either conflate the two or, worse, double-count them against Shopify's order total. The result is a board deck that says email contributed 38% of revenue when the honest number is 22%, or vice versa. If you can't tell the difference, you can't make a budget decision off it.

This post is a technical walkthrough of how Klaviyo actually attributes revenue, why most cross-tool dashboards get it wrong, and how to build a single attribution model that separates Flow Revenue from Campaign Revenue without double-counting against Shopify.

The technical reality — what Klaviyo measures

Klaviyo's Attribution Reporting docs describe two distinct revenue surfaces with different attribution windows and different click semantics.

Flow Revenue is revenue attributed to an order that follows a flow message — an automated, trigger-based send (welcome series, abandoned cart, browse abandonment, post-purchase, win-back). The attribution window is configurable per account but defaults to 5 days for email and 24 hours for SMS, measured from the moment Klaviyo sent the flow message that the recipient interacted with.

Campaign Revenue is revenue attributed to a one-time send (a broadcast — a Black Friday email, a product launch, a back-in-stock blast). Same default windows (5 days email / 24 hours SMS), same click-based attribution model.

The mechanics are click-then-conversion: a recipient clicks a tracked link in the email, Klaviyo drops a __kla_id parameter on the destination URL, and any Shopify order placed from that visitor's session within the attribution window is credited back to Klaviyo's send. If a customer opens both a flow message and a campaign within the same window and clicks both, the most recent click wins — single-touch, last-click within Klaviyo's universe.

Two things are critical and routinely missed:

  1. Open-based attribution is off by default. Klaviyo offers an opt-in "Opens + Clicks" attribution model, but its own documentation cautions that Apple Mail's Mail Privacy Protection (MPP) auto-loads tracking pixels, which means opens are no longer a reliable signal of intent. Most accounts should leave this on the default click-based model. (We have a longer write-up of MPP's downstream effects in our iOS 15 deliverability post.)
  2. Klaviyo's revenue numbers are Klaviyo's view of the truth. They don't reconcile to Shopify's order_total by default. Shopify's order has one source-of-truth value; Klaviyo attributes some portion of that order to its own send via the click-attribution chain.

Why it matters for DTC attribution

The DTC operator's blended-MER calculation requires every channel to declare a revenue number and for those numbers to not overlap with each other. Email is uniquely prone to overlap because:

The most common pathologies we see:

The mechanical consequence: email's share of revenue gets inflated by 30-50% on paper, blended MER looks worse than it is (because paid is dividing into a true revenue number while email is dividing into a phantom one), and the operator scales paid spend on a false signal.

Methodology — how to measure this yourself

Here is the replicable, no-magic-numbers way to separate Flow Revenue and Campaign Revenue without double-counting against Shopify. You can apply this to any DTC stack — Klaviyo + Shopify is the most common; the same pattern applies to Omnisend + Shopify, Mailchimp + WooCommerce, etc.

Step 1 — Pick Shopify's order_total as the canonical revenue number. Every other channel's "attributed revenue" is a share of this, not an addition to it. Write down: total_revenue = shopify.order_total.

Step 2 — Pull Klaviyo Flow Revenue and Campaign Revenue from the Reports API, separately. Klaviyo's Reports API returns Flow Revenue and Campaign Revenue as separate metrics. Pull both with the same date range. Confirm they are non-overlapping by spot-checking 10 recent orders: for each order, Klaviyo's UI shows which (single) send it was attributed to. If you see overlaps, you have an attribution-model config issue, not a measurement issue.

Step 3 — Reconcile to Shopify by joining on order_id. For every Shopify order in your window, ask: was it attributed to a Klaviyo Flow? A Klaviyo Campaign? Neither? Bucket every order into exactly one of three columns:

The sum of the three columns must equal Shopify's order_total. If it doesn't, you have either (a) a duplicate order in Klaviyo's attribution log (rare), or (b) a join issue between Klaviyo's order_id and Shopify's order_id (more common — Klaviyo stores it as a string, Shopify stores it as a numeric, and a naive join drops orders).

Step 4 — Compute email's true share. email_share = (email_flow_attributed_revenue + email_campaign_attributed_revenue) / total_revenue. This is the only number you should report as "email's contribution to revenue." It is by construction a subset of Shopify's total — no double-counting.

Step 5 — Treat Flow and Campaign as separate budget lines. Flow Revenue is a near-zero-marginal-cost channel (you built the flow once; every triggered send is free). Campaign Revenue is a marginal-cost channel (each send has list-hygiene cost, deliverability cost, unsubscribe cost). They should be evaluated separately because the ROI math is fundamentally different.

Illustrative scenario — how the numbers shift after the fix

Imagine a DTC apparel brand running on Shopify + Klaviyo + Meta + Google. Their dashboard before the fix reads:

This is the double-counting symptom: the sum of platform-attributed revenue exceeds the truth. The operator now sees email contributing $320,000 of $1,250,000 = 25.6% of revenue.

After applying the reconciliation methodology above:

The before-fix dashboard inflated email's share by ~3.6 percentage points and inflated total revenue by 25%. The blended-MER calculation built on the inflated number was wrong, and the operator's "email is killing it, scale email" thesis was a measurement artifact. The numbers above are illustrative — they show the direction of the correction, not a population statistic.

What we do at Admaxxer

Admaxxer's Klaviyo connector pulls Flow Revenue and Campaign Revenue separately from Klaviyo's Reports API, joins them to Shopify's order_id server-side, and surfaces a single non-overlapping revenue model in the dashboard. Email's share is computed as a fraction of Shopify's canonical revenue — no addition, no double-counting. See our revenue tracking overview for how the join works. The revenue data flow doc shows where Shopify and Klaviyo feed into the same canonical orders table. If your blended MER stops moving when you ramp paid, this is the first thing we'd investigate.

FAQ

How does Klaviyo decide between Flow and Campaign credit when both could attribute?

Klaviyo uses last-click within its 5-day default window. Whichever Klaviyo-tracked link the recipient clicked most recently — flow or campaign — gets the credit. The other send is recorded as "sent but not attributed." This is single-touch attribution within Klaviyo's universe. If you want multi-touch within email, you have to build it yourself from raw click logs.

What if a customer clicks both a Klaviyo email and a Meta ad in the same window?

Klaviyo doesn't know about the Meta click. Klaviyo attributes the order to its send. Meta also attributes the order to its ad. Your blended dashboard sees both claims. The reconciliation step (joining everything against Shopify's canonical order_id, then applying a single attribution model on top) is how you avoid double-counting.

Should I include opens in Klaviyo's attribution model?

Default to no, for the reason Apple's MPP made open data unreliable. Klaviyo's click-based model is more honest. If you must include opens, only include them for non-Apple Mail clients (filter on the recipient's mail client) — but this gets complicated fast and the lift is marginal.

What's the right Klaviyo attribution window?

Klaviyo's default is 5 days for email. If you're a low-AOV / fast-purchase-cycle brand (consumables, fashion), 3 days is more honest. If you're a high-AOV / long-consideration brand (furniture, electronics), 7-14 days is defensible. The window should match how long your audience typically takes to convert after first email touch. Klaviyo's attribution settings docs walk through how to change it.

How do I check if my dashboard is double-counting right now?

The simplest test: sum every channel's attributed revenue for last month. If the sum exceeds Shopify's order_total, you're double-counting. The gap is your over-attribution. (A small gap of <5% is usually OK and reflects multi-platform clicks; a 20-30% gap means a fundamental model error.)

Does this matter if I'm not running paid ads?

Yes. Flow vs Campaign Revenue separation matters for internal email-program decisions: should I invest in more flows or more campaigns? Without separation, you can't make the call. Even brands that are 100% organic + email need the split.

How does this interact with SMS attribution in Klaviyo?

Klaviyo treats SMS as a separate channel with its own attribution column ("SMS Flow Revenue" and "SMS Campaign Revenue"). The same double-counting risk exists between SMS and email — if a customer receives both an abandoned-cart email and an abandoned-cart SMS within the 5-day window and clicks the SMS, the order is attributed to SMS, not email. Your reconciliation methodology should bucket both email and SMS attributed orders against Shopify's canonical revenue, and the channel totals (email + SMS + paid + organic) should sum to Shopify's order_total, not exceed it. SMS's default click window is 24 hours, not 5 days — even shorter, which means SMS attribution overlap is mechanically smaller than email overlap.

What about post-purchase flows — should those revenue numbers be in the "email contribution" line?

Post-purchase flows are a special case. The order that triggered the post-purchase flow is not attributable to email — the customer already converted. But the second purchase triggered by, say, a 30-day post-purchase upsell flow is attributable to email. The cleanest model: post-purchase flow revenue is a separate line ("repeat-purchase email revenue") that's evaluated against LTV contribution, not first-purchase CAC. Mixing the two collapses the distinction between acquisition email (which competes with paid CAC) and retention email (which competes with operational retention spend).

How granular should our attribution be — per-flow or aggregate?

Aggregate is fine for the board-level revenue split, but the operational decisions (which flow to invest in, which to deprecate) happen per-flow. Klaviyo's Reports API returns Flow Revenue by flow ID, so the per-flow view is one API call away. The most actionable view we've seen is a per-flow table sorted by revenue contribution and emission cost — abandoned cart and welcome series usually dominate revenue; product-launch broadcasts dominate emissions. A flow at the bottom of both rankings is a candidate for deprecation.

klaviyo email_attribution shopify dtc mer
Try Admaxxer Free