Home Services Pricing About Us Apps & Tools Blog Get Started
Back to Blog
Data & Metrics

How to Calculate ACV in HubSpot Deals (No Code Required)

HubSpot's built-in Amount field is just a total contract value. It doesn't know whether that money spans 6 months or 3 years. Here's how to build two calculated properties that automatically derive contract length and annualized revenue from your deal dates.

By Solution Spot
May 2026
6 min read
All Levels
The Short Version

You need two calculated Deal properties: Contract Terms (Month) (derived from your start and expiration dates) and ACV (which divides Amount by the number of years in the contract). No workflows, no code, no manual entry. The formulas do the math automatically on every deal.

Property 1: Contract Terms (Month)

if((is_known(Contract Start Date) && is_known(Contract Expiration Date)), round_nearest( ((((((Contract Expiration Date - Contract Start Date) / 1000) / 60) / 60) / 24) / 30.44, 1))

Property 2: ACV

if(((is_known(Contract Terms (Month)) && is_known(Amount)) && (Contract Terms (Month) > 0)), if((Contract Terms (Month) >= 12), (Amount / (Contract Terms (Month) / 12)), Amount), if(is_known(Amount), Amount))

The Problem With Amount

HubSpot ships with a standard Amount field on deals. It's a number. That's it. There's no concept of time baked in. A $30,000 deal could be a single month of work, a quarterly subscription, a two-year SaaS contract, or a one-time professional services engagement. HubSpot treats them all identically.

This creates a real problem the moment you try to report on sales performance. Your pipeline looks great at $1.2M, but what's the annual run rate? If your reps are closing a mix of 12-month and 36-month deals, comparing them by total amount is meaningless. A $36,000 three-year deal isn't "better" than a $15,000 one-year deal. The 12-month deal has a higher ACV.

ACV (Annual Contract Value) is the metric that normalizes this. It answers: if this deal renewed annually at its current rate, what would we recognize each year? It's the standard way SaaS companies track bookings, and it's nowhere in HubSpot by default.

Don't Confuse ACV with TCV

TCV (Total Contract Value) is what HubSpot's Amount field stores: the full value over the life of the contract. ACV is that number annualized. A $36,000 2-year contract has a TCV of $36,000 and an ACV of $18,000. Both are useful, but they answer different questions, and conflating them will wreck your revenue reporting.

What You're Building

The solution is two calculated Deal properties that work together. HubSpot's calculated properties can read other fields on the same record and do math on them, including date math. Here's the chain:

  1. Contract Terms (Month): reads Contract Start Date and Contract Expiration Date, calculates the difference in months, and stores it as a number
  2. ACV: reads Amount and Contract Terms (Month), then annualizes the value if the contract is 12 months or longer

You need both, in that order, because ACV depends on Contract Terms being calculated first. Once they're set up, both fields update automatically whenever the underlying dates or amount change. No workflows, no manual entry.

You'll also need Contract Start Date and Contract Expiration Date properties on your deals. These might already exist in your portal, so check your Deal properties before creating duplicates. If they don't exist, create them as Date type properties.

Property 1: Contract Terms (Month)

Go to Settings → Properties → Deals and create a new property. Set the field type to Calculation, the calculated property type to Custom equation, and the output type to Number. Use Unformatted number as the number format.

Here's the formula:

if((is_known(Contract Start Date) && is_known(Contract Expiration Date)), round_nearest( ((((((Contract Expiration Date - Contract Start Date) / 1000) / 60) / 60) / 24) / 30.44, 1))

What this formula does

Rather than dividing by a magic millisecond constant, this formula walks down the conversion chain step by step: date subtraction gives you milliseconds, then / 1000 gives seconds, / 60 gives minutes, / 60 gives hours, / 24 gives days, and finally / 30.44 gives months (30.44 being the average days per month). It's the same math as dividing by 2,628,000,000, just broken into readable steps.

round_nearest(..., 1) rounds to one decimal place so you get clean values like 21.4 or 12.5 instead of 21.3863...

The outer if(is_known(...)) guard ensures the property returns blank (not zero, not an error) when either date is missing, which is exactly what you want for deals that haven't been fully qualified yet.

Property 2: ACV

Create a second calculated Deal property. Same setup (Calculation, Custom equation, Number) but this time set the number format to Currency. You can optionally check "Use record currency instead of company currency" if you deal in multiple currencies.

Here's the formula:

if(((is_known(Contract Terms (Month)) && is_known(Amount)) && (Contract Terms (Month) > 0)), if((Contract Terms (Month) >= 12), (Amount / (Contract Terms (Month) / 12)), Amount ), if(is_known(Amount), Amount) )

Breaking down the logic

The formula has three branches, which it evaluates top to bottom:

  1. If both Contract Terms and Amount are known, and Terms > 0:
    • If the contract is 12 months or longer → annualize it: Amount / (Terms / 12)
    • If the contract is under 12 months → ACV equals Amount (sub-annual contracts aren't annualized; the TCV is the ACV)
  2. If Terms is missing but Amount is known → fall back to Amount (handles deals without dates filled in)
  3. If neither is known → blank

The annualization math is Amount / (Terms / 12). Dividing Terms by 12 converts months to years, so a 21.4-month contract becomes 1.783 years. Then Amount divided by that gives you the per-year value.

Why Not Just Multiply by 12/Terms?

The math is equivalent: Amount / (Terms / 12) is identical to Amount * (12 / Terms). The formula uses the division form because it reads more naturally as "amount per year," but either works. HubSpot's formula engine handles both fine.

Real-World Example

Here's what it looks like on an actual deal:

Example Deal
Amount $8,500.00
Contract Start Date 05/01/2026
Contract Expiration Date 02/10/2028
Contract Terms (Month) 21.4 months
ACV $4,766.36
Monthly Recurring Revenue $397.20

Let's verify the math manually:

Contract length: 05/01/2026 → 02/10/2028 = 21.4 months

Terms in years: 21.4 ÷ 12 = 1.7833 years

ACV: $8,500 ÷ 1.7833 = $4,766.36 ✓

MRR: $8,500 ÷ 21.4 = $397.20 ✓

Without ACV, this deal would show up in your pipeline as $8,500. But the annualized revenue is only $4,766. If you're comparing this to a $5,000 one-year deal, the one-year deal is actually worth more on an annual basis. ACV surfaces that instantly.

Bonus: Monthly Recurring Revenue

While you're in the property editor, you might as well build a Monthly Recurring Revenue (MRR) property too. It's the simplest formula of the three:

if((is_known(Contract Terms (Month)) && is_known(Amount) && (Contract Terms (Month) > 0)), (Amount / Contract Terms (Month)), if(is_known(Amount), Amount) )

This divides total Amount by the number of months to get a per-month figure. Format it as Currency. On the example deal above: $8,500 ÷ 21.4 months = $397.20/month, which you can see in the screenshot matches exactly.

With ACV and MRR both on your deals, you can build pipeline reports that slice revenue any way you need: by month, by year, by ARR contribution, or by sales rep, all pulling from the same source-of-truth dates and amount on each deal.

Gotchas & Edge Cases

The formula won't show up on a deal until the dates are filled in

Calculated properties evaluate in real-time against the current field values. If Contract Start Date or Contract Expiration Date is blank, Contract Terms returns blank. Since ACV depends on Contract Terms, ACV will also be blank. This is intentional and correct behavior: you don't want a $0 ACV showing up on a deal that just hasn't been dated yet. As soon as the dates are added, both calculated fields populate automatically.

The 30.44 day average is still an approximation

The formula uses 30.44 as the average days per month (365.25 ÷ 12). Actual calendar months vary from 28 to 31 days, so results can drift slightly on contracts that don't land on clean month boundaries. For sales reporting and ACV purposes this is accurate enough, and it matches what HubSpot's own native Time Between property displays.

Sub-annual contracts

The ACV formula treats contracts shorter than 12 months as equal to their Amount. Some companies prefer to annualize everything, so a 6-month $6,000 contract would show an ACV of $12,000. If that matches your business model, remove the >= 12 branch and always use the annualization formula. Just make sure your reporting team understands what the number represents.

Deals without expiration dates

Month-to-month or evergreen contracts don't have a natural expiration date. The formula leaves ACV blank for those deals. You can handle this separately. For example, by setting a convention of always entering 12 months forward as the expiration date for MTM deals, or by treating Amount as ACV for any deal flagged as "month-to-month" using a separate picklist property.

Ready to Use

Once both properties are created, add them to your deal record views, your pipeline views, and any deal-based reports you care about. They calculate immediately on save and stay current automatically. No workflows required, no manual data entry, no sync delays.

If you want to track ACV changes over time, for example, to detect when contract values are revised mid-term, that's where a tool like PrevVal comes in. But for getting a clean ACV number on every deal? This is the whole setup.

Work With Us

Need HubSpot built right
the first time?

Tell us what you need. We'll scope it, price it, and tell you exactly how we'd build it. Free call, no obligation, no sales pitch.