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)
Property 2: ACV
In This Article
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.
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:
- Contract Terms (Month): reads Contract Start Date and Contract Expiration Date, calculates the difference in months, and stores it as a number
- 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:
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:
Breaking down the logic
The formula has three branches, which it evaluates top to bottom:
- 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)
- If the contract is 12 months or longer → annualize it:
- If Terms is missing but Amount is known → fall back to Amount (handles deals without dates filled in)
- 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.
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:
Let's verify the math manually:
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:
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.
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.