September 2, 2026 · 9 min read
A co-termed upsell is two line items, and only one of them is recurring
Someone in a HubSpot practitioner room asked how to model a mid-contract upsell without Contracts or Revenue Hub. The answer is short. The reason it matters is the two dates nobody is checking.
This started as a question in a HubSpot practitioner community. A company had moved to annual pricing, had customers buying extra things mid-contract, and wanted those add-ons to end on the same date as the original agreement. They were not using Quotes, Contracts or Revenue Hub, HubSpot was not running their billing, and what they wanted from it was honest reporting: the upsell, the ARR, the contract value, and a renewal date they could trust. I have written the answer up here because the model is simple and the reasoning behind it is the part that usually gets skipped.
Two words first
- Co-term
- Lining up something new so it ends on the same date as a contract the customer already has. One renewal date for everything, instead of a new one every time they buy. Signed in January for twelve months, buys an add-on in April: co-terming makes the add-on end in December too, and December is the co-term date.
- Stub
- The short, odd-length period between when the add-on starts and the co-term date. April to December is a nine-month stub. It is a partial term rather than a full one, it only happens once, and after it everything renews together on a normal cycle.
What the customer is actually buying
Take Product A at 12,000 a year, sold to a customer with nine months left. Commercially they are buying two things, and the reason the modelling gets awkward is that people try to make one line item carry both. The first thing is nine months of Product A, billed once, now. The second is Product A on a full annual term, starting on the co-term date and renewing with everything else. The stub ends exactly where the ongoing commitment begins, which is why the annual line's billing start date is the same date as the original renewal.
The instinct that gets people into trouble is to put a nine-month term on an annual-frequency line, and when HubSpot's product configuration refuses that, to reach for a monthly-frequency line at the annual price divided by twelve instead. HubSpot accepts every field on that second version, which is what makes it look like the answer.
Two ways to model the stub, and what the deal reports
Rather than guess, I pulled the calculation formulas HubSpot defines on the line item object for ARR, MRR and total contract value. They are HubSpot-defined, so they are identical in every portal. On a monthly-frequency line, MRR is the unit price and ARR is the unit price times the term in months, capped at twelve. On an annual line, ARR is the unit price divided by twelve times the term, capped the same way. A line with no billing frequency at all contributes zero to both and its full amount to contract value. Contract value on any line is amount times number of payments.
| Stub as a monthly line | ARR | MRR | TCV |
|---|---|---|---|
| Annual line, 12,000 annually, 12 months | 12,000 | 1,000 | 12,000 |
| Stub, 1,000 monthly, 9-month term | 9,000 | 1,000 | 9,000 |
| Deal rollup | 21,000 | 2,000 | 21,000 |
| Stub as a one-time line | ARR | MRR | TCV |
|---|---|---|---|
| Annual line, 12,000 annually, 12 months | 12,000 | 1,000 | 12,000 |
| Stub, 9,000 once, no frequency | 0 | 0 | 9,000 |
| Deal rollup | 12,000 | 1,000 | 21,000 |
So the monthly version does not break anything HubSpot will tell you about. It breaks the two numbers the company said it wanted to trust, and it does so quietly, because every field validates. Contract value is right under both, which is part of why the problem survives: the report someone checks first looks fine.
How each line is configured
| Field | Line 1, the stub | Line 2, the ongoing commitment |
|---|---|---|
| Product | Product A co-term, a companion one-time product with unit price = annual ÷ 12 | Product A |
| Unit price | 1,000 | 12,000 |
| Quantity | 9, the months remaining | 1 |
| Billing frequency | empty, one-time | Annually |
| Term | empty | 12 months |
| Billing start date | empty, bills on close | the co-term date |
| Delayed billing start | empty | empty, never alongside a start date |
| Prorate | off, the quantity already does it | off |
| What it computes | ARR 0, MRR 0, TCV 9,000 | ARR 12,000, MRR 1,000, TCV 12,000, end date = start + 12 months |
One thing to check in your own portal
Whether your line items are getting a term at all. A line with a frequency and no term computes as twelve months, which happens to be right here, but it is worth knowing rather than assuming. And setting a billing start date and a delayed start on the same line makes the start-terms property compute to nothing, with no warning. A line that has a start date and a blank start-terms property has been double-set.
Where every date lives, and who puts it there
This is the part I would actually spend the hour on. Seven dates and quantities touch a co-termed upsell. Five of them can be computed or copied by the system. Two of them are typed by a person into a field that has no view of the deal behind it, and nothing checks them.
| The date | Where it is stored | Who writes it | Where the rep sees it |
|---|---|---|---|
| Original renewal date | Original deal, the renewal property you already have | Typed once, at the original close | The original deal record |
| Contract end date on the customer | Company, a custom date property | Workflow, copied up from the deal that closed won | The company record, visible from any deal. One date per customer |
| Co-term date on the upsell | Upsell deal, a custom date property | Workflow, copied from the associated company when the deal is created | The deal's own properties. Make it required before the stage where lines are added |
| Months remaining | Upsell deal, a calculated time-between property | Computed from close date and co-term date | Read-only on the deal, beside the co-term date |
| Stub quantity | Line 1, quantity | Typed by the rep | The line item editor. Nothing checks it against months remaining |
| Annual line billing start | Line 2, billing start date | Typed by the rep | The line item editor, under billing terms. Nothing checks it against the deal |
| Next renewal date | Line 2, billing end date, calculated | Computed as start plus term | On the line item. Roll it to the deal and the company so the next upsell starts from it |
What enforces the two manual entries
There are three levels, and each removes a piece of the manual process. Only the last removes the person from the date arithmetic entirely, and it is worth being clear-eyed about which one you are buying.
Level one, native, this week
Make the co-term date required on the deal before the line-item stage, so a deal cannot carry lines without one. Calculate months remaining on the deal and show it beside the co-term date, so the rep reads the stub quantity off the record rather than working it out. Build a saved view of line items on open upsell deals with billing start date next to the deal's co-term date, so a mismatch is visible in one glance. None of that stops a wrong entry. It puts the right answer in front of the person at the moment they type, which is more than most portals do, and in my experience it is most of the damage.
Level two, custom code
When a line item is associated to an upsell deal, a custom code action reads the deal's co-term date and months remaining and writes the stub quantity and the annual line's billing start date itself, then re-checks on every edit and flags a deal whose lines disagree with its date. That needs Operations Hub Professional, or line item workflows if your portal has them, which I have not verified for every tier. It is what a system enforcing the date looks like without Contracts. Someone owns the code, and the co-term date is still a property a person could overwrite.
Level three, Contracts
The contract holds the end date, and there is no copy on the company or the deal to drift. The upsell is a change on the contract, and the system prorates it to the contract's end date, so nobody types nine and nobody types a start date. The renewal quote inherits the contract's dates. What you take on instead is a beta surface, and HubSpot holding the agreement of record alongside whatever holds the signed document today.
Without Contracts, with Contracts
So, to the question I was actually asked: no, the Contract object is not essential for the reporting, and I would not bring Contracts or Revenue Hub in to solve a reporting problem when HubSpot is not running the billing. The line items are the same shape either way. What changes is which object holds the agreement and who does the date arithmetic.
| Without Contracts | With Contracts (Revenue Hub, beta as of September 2026) | |
|---|---|---|
| The agreement | No record of its own. Implied by the original deal plus its close date and term, with the signed document wherever you keep it | A Contract record with an effective date, a term and an end date |
| Where the co-term date lives | On the original deal, copied to the Company by workflow. A customer with several live agreements keeps it on the original deal and links upsells with a deal-to-deal association | The contract's end date. Nothing to copy |
| The two lines | One-time stub at the prorated amount plus an annual line starting on the co-term date | The same two lines, added to the contract by the amendment |
| ARR, MRR, TCV | 12,000, 1,000, 21,000 from the line item formulas | Same numbers, same formulas, rolled to the contract as well as the deal |
| The next renewal date | Computed on the annual line as start plus term | Held on the contract and inherited by the renewal quote |
| Mid-term change history | Each upsell is its own deal. What changed since signing is a list of deals against the company | Each change is recorded on the contract and mints an order carrying only the delta lines |
| Billing schedule | Not in HubSpot, which is the situation today | Generated from the contract's lines, if you want HubSpot to own it |
| What you give up | One date per company. Change history reconstructed from deals. Two entries a person types | A beta surface. The changes endpoint moved once in August 2026 without notice |
Build the two lines now, put the date somewhere a new deal can reach, and moving to Contracts later is a migration of the agreement record rather than of the line items. If you spend a year with co-term dates that exist only implicitly in deal history and then move, you are reconstructing every agreement's term from a chain of deals, and that is very cheap to avoid today.
The formulas above were read from the line item property schema on 2 September 2026. The Contracts column describes the model as HubSpot has published it and as I have seen it behave; I did not test it against a live portal for this piece, and given how recently it moved I would re-read it before building on it.