[object Object]

Every HubSpot implementation eventually hits the same fork. Deal-level custom properties to track product, plan, term, MRR, ARR, discount. Or line items as the source of truth with deal totals derived. The wrong call lives in your portal for years and corrupts every report you build on top of it.

The honest answer is not “always use line items.” The honest answer is conditional, and most teams pick wrong because the line item UX is worse than the property UX. Convenience is not the criterion.

The default rule

If you sell more than one SKU per deal, ever, line items win. If you sell exactly one SKU per deal, properties win.

That is the framework. Everything below is the qualification.

Why properties feel right and lie

Sales reps love deal properties. One screen, one save, done. Forecasting reports filter cleanly on a single property. Workflows trigger on a property change without traversing associations.

The lie shows up when finance asks: “What was our renewal ARR by product line last quarter?” If product lives on the deal, you have one product per deal. Either you bloat your portal with hundreds of “Product 1, Product 2, Product 3” properties, or you stitch SKUs into a multi-select, or you create duplicate deals. All three are bad. The third is criminal.

Why line items feel wrong and are right

Line items split each SKU into its own record with quantity, unit price, discount, and term. They feed quote totals, fuel proper revenue reporting, and survive the inevitable “we cross-sold three add-ons on this deal” reality.

The pain is real. The standard line item UI on the deal record is a sub-table, not a first-class object. Reports require unwinding associations. Workflows on line items are limited compared to deals.

The decision tree

1. One SKU per deal forever, no upsell, no cross-sell?
   → Deal properties. Stop here.

2. Multiple SKUs per deal, but pricing is uniform per deal?
   → Deal properties + a line items mirror for finance.

3. Multiple SKUs, per-line discounts, mixed terms?
   → Line items as source of truth. Roll up to deal.

4. Recurring revenue with mid-term changes?
   → Custom object (subscription) + line items. Deals are deal-shaped, not subscription-shaped.

Case 4 is where teams discover deals were never the right primitive. We have a separate write on this, but the symptom is “we keep cloning the deal to record an upgrade.”

The rollup pattern that does not lie

If you go line items, you need deal-level aggregates that survive line item edits. HubSpot’s built-in deal amount rollup is not enough because it does not split MRR vs one-time, or current term vs renewal.

Use a custom code action on line item create or update.

exports.main = async (event, callback) => {
  const dealId = event.inputFields["associated_deal_id"];

  const lineItems = await hsClient.crm.lineItems.searchApi.doSearch({
    filterGroups: [
      {
        filters: [
          {
            propertyName: "associations.deal",
            operator: "EQ",
            value: dealId,
          },
        ],
      },
    ],
    properties: [
      "amount",
      "hs_recurring_billing_period",
      "hs_term_in_months",
      "quantity",
      "price",
    ],
    limit: 100,
  });

  let mrr = 0;
  let oneTime = 0;

  for (const li of lineItems.results) {
    const p = li.properties;
    const lineTotal = Number(p.amount || 0);
    if (p.hs_recurring_billing_period) {
      mrr += lineTotal / (Number(p.hs_term_in_months) || 12);
    } else {
      oneTime += lineTotal;
    }
  }

  await hsClient.crm.deals.basicApi.update(dealId, {
    properties: {
      computed_mrr: mrr.toFixed(2),
      computed_one_time: oneTime.toFixed(2),
      computed_arr: (mrr * 12).toFixed(2),
    },
  });

  callback({ outputFields: { mrr, oneTime } });
};

Trigger this on the line item workflow, not the deal workflow. Deal workflows that try to read child line items race against the association write.

Forecasting implications

Deals roll up to forecast. Line items do not, directly. If you put product on the line item, your forecast by product needs a custom report unwinding associations.

Make peace with the custom report or stop trying to forecast by product. Both are valid. The thing that is not valid is forecasting on a deal-level “primary product” property that is wrong half the time because reps update line items and forget the property.

Quote and CPQ angle

If you use HubSpot quotes, line items are not optional. Quotes generate from line items. A deal with product on a property and no line items will produce a quote with no body. Sales discovers this on Friday at 4pm.

If you use a separate CPQ tool, line items still tend to win because the CPQ sync object is closer to a line item shape than a deal shape.

Migration gotchas

Migrating from property-based to line-item-based mid-portal is a real project. The traps:

  • Historical deals lose product context if you only backfill amount. Backfill quantity, price, and the SKU code, or your historical ARR report drifts.
  • Discount math on a deal-level property does not split cleanly across multiple line items. Decide if discount is per-line or per-deal and document it before migrating.
  • Currency conversion on multi-currency portals applies at line item level once you migrate. Old deal-level FX rates may not match.

When to use both

Use both when one property answers a single high-frequency question, like “is this deal a renewal.” That is a deal-level fact, not a per-line fact. The cardinal sin is duplicating per-line product detail at the deal level. Single facts up top, multi facts below.

Related reading: deal pipeline optimization for the upstream side and forecast accuracy discipline for the downstream side.

Bottom line

  • One SKU per deal forever, properties win on simplicity.
  • Multi-SKU, per-line discount, or mixed term, line items are source of truth.
  • Build deal-level MRR and ARR rollups via custom code on line item events, not deal events.
  • Quotes and CPQ make line items mandatory whether you like it or not.
  • Subscriptions outgrow deals; use a custom object before you start cloning deals for upgrades.
[object Object]
Share