Skip to content

Account value (LTV)

When an Account converts to customer, LeadHunter records its expected lifetime value alongside the close event. That single number powers the ROI rollups on channels and campaigns — without it, you’d see how much you spent but not how much you captured.

This page explains the three layers that feed the value, when each one wins, and how to set up sensible defaults so most accounts auto-fill at conversion time.

The same Account can have its value set at three levels, with this precedence:

LayerWhen it winsWhere to set it
AccountAlways when set — sticky.Account detail page → Captured value field.
CampaignFalls back to the campaign that produced the customer when the Account doesn’t have its own value.Campaign settings → Account value override.
ProductFinal fallback when neither Account nor Campaign carries a value.Product detail page → Default account value.

Operator-set values are sticky. If you type a value into Account → Captured value, future auto-snapshots never overwrite it. This is the right default for B2B deals where the actual contract value matters more than any rule of thumb.

When you change an account’s status to customer (manually, via API, via a CSV import that sets the status field), LeadHunter:

  1. Checks if the Account already has a captured value set. If yes → done; nothing changes.
  2. Looks for the most recent campaign that account participated in, and checks for a campaign-level value override. If found → snapshot that value onto the Account, with source = Auto: Campaign override.
  3. Otherwise looks for the account’s product (via any campaign membership) and uses the product default. If found → snapshot, with source = Auto: Product default.
  4. If none of the above resolve, the Account is left without a captured value — you can fill it in by hand at any time.

The snapshot is frozen at conversion time. Changing the product default next month does not retroactively update historical customer values — the same way you wouldn’t restate last quarter’s revenue when your price list changes. Each Account remembers what its value was when it closed.

Re-affirming a customer doesn’t re-snapshot

Section titled “Re-affirming a customer doesn’t re-snapshot”

If you change status to customer on an account that’s already a customer (a CSV re-import, a data sync that touches the row again), the snapshot is a no-op. Existing values stay put. The status-change audit trail entry is still appended, so you can see when the import touched the row.

Every customer Account carries a captured_value_source tag explaining how the value got set:

SourceWhat it means
Manually enteredAn operator typed the value (or it came in via API / CSV with the field set). Sticky — won’t be overwritten by future auto-snapshots.
Auto: Campaign overrideThe campaign that produced the customer had a value override set, and LeadHunter snapshotted it.
Auto: Product defaultNo campaign override; the product’s default value was used.
(blank)No value captured. Either the account is a customer without product/campaign context (pure-inbound), or the product/campaign didn’t have defaults configured when the close happened.

The dashboard breakdown shows the By source distribution per channel and per campaign, so a glance tells you whether you’re mostly auto-snapshotting from defaults or manually entering deal values.

Each layer carries its own currency:

  • Product: default_account_value_currency (ISO 4217 — EUR / USD / GBP / …).
  • Campaign: account_value_currency — falls back to the product’s currency when blank.
  • Account: captured_value_currency — snapshotted along with the amount.

No FX conversion happens. Rollups group by currency, and the ROI calculation only runs when the channel’s / campaign’s spend currency matches the captured-value currency for a given account. Mixed currencies surface a roi_unavailable_reason explaining which currencies are present.

If your product is priced in USD but you’ve logged Adwords spend in EUR, the channel’s ROI will explicitly tell you the currencies differ and skip the math rather than producing a misleading number.

Channel detail pages show the value summary — total captured value across every customer Account that was attributed to that channel, with currency breakdown and the by-source distribution. Pair it with the cost summary on the same page and you get ROI = value / spend in the same primary currency.

Campaign detail pages carry the same shape — total customer value attributed to that campaign, vs. the campaign’s logged expenses (labor, agency, content, event, etc.). The ROI block computes campaign-level return when spend and value share a currency.

  • A channel has at most one Account attached via the acquisition_channel FK, so a customer’s value counts once per channel.
  • A campaign can share an Account with another campaign (the same lead in two outreach efforts). That customer’s value will appear in both campaigns’ rollups — each campaign gets credit for the full deal. Useful for ROI math at the campaign level; not the right shape for a company-wide rollup where you’d want first-touch / last-touch / linear attribution. Multi-touch attribution is on the roadmap.

A few practical patterns:

SMB self-serve product with predictable pricing. Set Product.default_account_value to your average customer LTV. Every customer auto-snapshots at that number. Operators only override when a specific deal is unusually large or small.

Mixed self-serve + enterprise. Set the product default for SMB. Create an enterprise campaign and set Campaign.account_value_override to the enterprise tier’s expected LTV. Customers closed via the enterprise campaign auto-snapshot at the higher number; everything else falls back to the SMB default.

Long-cycle B2B with hand-negotiated deals. Leave the defaults blank (or set conservative placeholders) and have the operator enter the actual deal value at close. The auto-snapshot does nothing because the value is already manually set. You get accurate per-deal numbers at the cost of operator effort.

Pure-inbound (no campaign context). Pure-inbound customers (Adwords lead that converted without ever being added to an outbound campaign) won’t have a product to fall back to — there’s no campaign membership to read the product from. Operators need to enter the value at close. The status change still triggers the snapshot, but it resolves to “no value” and silently skips.

  • Churn or contract endcaptured_value is a single number at conversion. If a customer churns or expands, the value doesn’t update automatically. Operators can edit it any time; a structured value-history journal is on the roadmap.
  • MRR vs. ACV vs. contracted value — the field is just a number. Decide which definition you’re using and stick with it across all customers of a product so the rollups are comparable.
  • Discounting — if the SMB self-serve product is on a discount this quarter, the product default doesn’t know. Update the default when you change prices, or override at the campaign level for the discount-period campaign.
  • Value history journal — when a customer churns or expands, record the change with a timestamp so the rollup can reflect net captured value over time, not just the conversion snapshot.
  • Per-acquisition cost delivered by ad-platform CPA data — pairs with channel webhook ingestion. Today’s ROI rolls up channel-level spend; per-account cost would let you see ROI per deal.
  • Cohort ROI“of customers acquired in May, what’s the cumulative captured value vs. May spend over the following 6 months?” The current rollup is point-in-time; cohort ROI needs a join on the conversion date.
  • Multi-touch attribution — split a customer’s value across the channels and campaigns that touched them, under configurable models (first-touch / last-touch / linear / time-decay).
  • Currency conversion — daily FX snapshots so cross-currency rollups can produce a single ROI number instead of refusing the math.