Ryan Ginsberg

September 5, 2026 · 7 min read

Read the container, not the line item

Seven pieces, one $9,995-a-month sale, fifteen line item records. Here's the reporting rule that falls out of it, and the property to trust on every container in between.

One sale. One product, one customer, $9,995 a month, six months. By the time it finished travelling through the quote-to-cash chain, quote, deal and order, subscription, six invoices, six payments, it existed as fifteen separate records in the Line Item object. Sum the dollar value on every one of them, the property called amount, and you get $149,925 against a contract worth $59,970. Sum a different property, the one meant to hold Monthly Recurring Revenue, hs_mrr, and it's worse: nine of the fifteen carry it fully populated, for $89,955 a month against a subscription that bills $9,995. Neither is a typo and no record is broken. Every one of the fifteen is doing what it was built to do.

That's the shape of the whole chain. What's left is the practical question. Given all of it, what do you actually put in a report?

The answer isn't to de-duplicate the Line Item object. One hop in that chain, deal to order, doesn't copy at all; it shares the same record, so a uniform de-dup rule under-counts exactly where it should count once. The answer sits one level up. Every container the line item feeds already has its own headline number, and some of those are genuine rollups, computed and locked, while others are ordinary fields a person or an import can type into and never touch again. Knowing which is which, on each container, is the whole job.

Which number is actually a rollup

Walk the containers this sale touched and ask one question of each: does its headline dollar figure derive from the line items underneath it, or is it a value sitting in a field that happens to look like a total?

ContainerHeadline propertyLocked (read-only)?Derived from the line items?
DealamountNo, plain writable numberNo. HubSpot offers to fill it in when you save line item changes in the UI, and writes it at no other time. An import skips the offer entirely.
Dealhs_tcv, hs_arr, hs_mrr, hs_acv (Total Contract Value, Annual Recurring Revenue, Monthly Recurring Revenue, Annual Contract Value)YesYes, an exact sum of the same-named line item property on every one of the 92 deals in the portal that carry a line item. Zero exceptions.
Quotehs_quote_amountNot confirmedYes, but which figure depends on a switch, hs_quote_total_preference.
Quotehs_tcvYesYes, and it's the one number on a quote that doesn't move with the switch.
Orderhs_total_price, hs_subtotal_priceNo, plain writable numberNo
Carths_total_priceNo, plain writable numberNo
Invoicehs_subtotalYesYes. HubSpot's own description: "The sum of all line items of the invoice including line item level taxes."
Subscriptionhs_total_mrr, hs_total_arrYesYes, a true rollup type on the schema (calculation_rollup), not a formula a human wrote.

Orders, invoices, products and carts carry none of the ACV, ARR, MRR, TCV family at all. That vocabulary exists on line items, on deals, on quotes (hs_tcv alone) and, in a richer form, on subscriptions.

The deal row is the one I'd spend a minute on. amount is what most people mean when they say "the deal size," and it isn't derived from anything. Five of the 89 deals in this portal that have both an amount and line items disagree with every candidate sum, in both directions, including one whose amount overstates its own line item total by 192 times, with nothing on the record flagging it. The four properties next to it never drift, because nothing writes them but the rollup itself.

The same sale, four different answers

Read the traced sale's own containers live and the gap isn't hypothetical:

ContainerPropertyValue
Quotehs_quote_amount$9,995
Quotehs_tcv$59,970
Dealamount$59,970
Orderhs_total_price (hs_subtotal_price is null)$59,970
Invoicehs_subtotal (one billing period)$9,995
Subscriptionhs_total_mrr / hs_total_arr$9,995 / $59,970

The quote and the deal disagree by 6x on the same sale, off the same line item, and the cause is two preference switches, one on each record. hs_deal_amount_calculation_preference, with options TCV, ARR, MRR and CUSTOM, hidden by default, sits null on this deal, and a null preference behaves as TCV, the whole contract, on every deal in this portal where the choice makes a difference. hs_quote_total_preference, with options TCV or TOTAL_FIRST_PAYMENT, whose option label reads "Use total first payment, saved in LineItem Amount," sits at TOTAL_FIRST_PAYMENT on this quote, one payment. If you've never heard of either property you're still governed by both. Neither reading is wrong. They're answering two different questions, and nothing on either record says which.

The order row is subtler. hs_total_price reads $59,970, matching the deal's contract value, but the match isn't a calculation, because hs_total_price is a plain writable number, the same as on a cart. The deal and the order do share the one line item that doesn't copy, and that share is an association rather than a calculation. The order's one line item reads amount of $9,995, one billing period, and hs_subtotal_price is null. Both the $59,970 and the $9,995 are true statements about the same record; neither is labelled, and nothing on the order reconciles them.

The invoice is the cleanest read in the table, $9,995, correctly scoped to the one billing period it covers. The subscription's hs_total_mrr and hs_total_arr land on the same figures as the deal's hs_mrr and hs_arr because both are genuine rollups reading the same numbers through two different mechanisms.

Annual doesn't mean what it sounds like

One more property earns a plain restatement before the checklist, because it's the one most likely to reach a board deck unchallenged: hs_arr. On the periodic billing frequencies, monthly, quarterly, annually and the multi-year options, the formula is hs_mrr × min(term_in_months, 12). Weekly and biweekly billing take a separate weeks-based branch and aren't covered by that shorthand, and a third branch handles prorated line items.

A six-month subscription at $9,995 a month reports hs_arr of $59,970, not the $119,940 an annualised run rate would suggest. That's the min() working as published, and I verified it across all 111 recurring line items in the portal at every term length present. The same cap runs the other direction on longer contracts: a three-year deal at that price reports hs_arr capped at twelve months' worth while hs_tcv keeps counting for the full 36. Two contracts with identical monthly price and different lengths report identical hs_mrr, then diverge on hs_arr and hs_tcv, correctly and by design, with nothing on either record telling you which one you're looking at.

What to actually report

Report on the container, not on the raw Line Item object, and know which switch and which rollup you're reading before you put a number in front of anyone who will act on it.

  • Deal size: read hs_tcv, or hs_arr and hs_mrr for a run-rate view, never amount, unless you've already reconciled amount against the line items yourself.
  • Quote total: check hs_quote_total_preference first. If it's TOTAL_FIRST_PAYMENT, you're reading one payment, not the deal. hs_tcv on the same quote gives you the full contract regardless of the switch.
  • Order or cart total: treat hs_total_price as a number someone typed, because that's what it is. It isn't derived from the order's line items and nothing checks it against them.
  • Invoice total: hs_subtotal is a genuine locked sum of that invoice's line items. Trust it for one billing period; don't sum it across invoices expecting a contract total without also checking how many periods have actually invoiced.
  • Subscription revenue: hs_total_mrr and hs_total_arr are real rollups. Confirm what associates to the subscription before trusting the number, since a subscription mid-scheduled-change can hold more than one line item at once.

If you're going to build a report directly off the Line Item object anyway, the one filter that makes it trustworthy is an association filter, and it has to be exactly one type. The deal association is the one I'd reach for by default: because a deal and its order share the single record that doesn't copy, filtering to "associated to a deal" returns each sold thing exactly once with no invoice or payment copies mixed in. Sum hs_tcv on those rows for contract value; amount on the same rows is one billing period. Filtering to "associated to an invoice" instead gives you a period-by-period read, correctly scoped to what was billed that month. Either works. No filter at all returns $3,215,069.04 across the 632 line items in this portal, a number that corresponds to nothing a finance team would recognise.

The part I'd want to work on

Fifteen records from one sale, and every one of them exists for a reason: a deletion-safety mechanism, a rollup HubSpot deliberately protects, a scheduled billing event that needs its own permanent record. None of it is broken. The object is coherent once you can see the whole chain, hop by hop, container by container, switch by switch. What was missing was anyone writing the chain down, and this series is my attempt at that.

The rule is short. Read the container's rollup, not the atom underneath it, and check the switch before you trust the number the switch produced.

More writing

Let's talk

You bet on HubSpot. It still isn't running your business.

That's the conversation I want — 30 minutes, no contract, no black box. Tell me what's actually broken and I'll tell you straight what I see. It's transcribed and shared back to you, so you keep a real read on where you stand and what I'd do first, whether or not we work together.

Not ready for a call? Email me your situation and I'll tell you straight whether you need me. No form, no sequence, nothing captured — if the honest answer is that you don't need me, that's the answer you'll get.