Contracts
Signed agreements with customers — terms, value, renewals and the automatic expiration sweep.
Contracts
A contract is the legal agreement between your company and a customer — what they buy, for how long, at what price, on what payment terms. In HotCRM, contracts are the system of record for active customer relationships: once activated, they unlock the Customer status on the account.
The 5 statuses
| Status | Color | What it means |
|---|---|---|
| ⚪ Draft | Gray | Being negotiated, not signed |
| 🔵 In Approval | Blue | Sent for internal sign-off |
| 🟢 Activated | Green | Signed and in force |
| 🟠 Expired | Orange | End date has passed |
| 🔴 Terminated | Red | Cancelled early |
Activation is the key transition — it triggers the account to be promoted to Customer.
The 6 contract types
| Type | Use for |
|---|---|
| Subscription | Recurring access — SaaS, software subs |
| Service | Time-bounded services — consulting, training |
| License | Perpetual software licenses |
| Partnership | Reseller, referral, channel partner agreements |
| NDA | Non-disclosure (zero-value) |
| MSA | Master Service Agreement — umbrella for many SOWs |
What a contract record stores
A contract record declares its fields in six groups. The contract form shows a curated subset of them across its tabs, so read this as the full inventory of what a contract carries rather than a screen-by-screen map:
| Group | What's in it |
|---|---|
| Contract Information | Contract Number (auto-numbered CTR-0001), Contract Type, Contract Document, Description |
| Parties | Contract Owner, Account, Primary Contact, Related Opportunity — the contact and opportunity pickers are scoped to the account you pick first |
| Terms & Dates | Contract Term (Months), Start Date, End Date, Special Terms |
| Contract Value | Contract Value, Billing Frequency, Payment Terms, Billing Address |
| Status & Approval | Status, Signed Date, Signed By |
| Renewal | Auto Renewal, Renewal Notice (Days) |
Account, Primary Contact, Contract Term, Start Date, End Date, Contract Value and Status are all required — a contract cannot be saved without them.
Three field names people go looking for and do not find:
- Activation Date. There is none, and no other field records the moment of activation. Activation stamps Signed Date instead (see On contract activation below), so that is the date any "when did this go live" report has to read.
- Renewal Terms. Renewal is exactly the two fields above. Free-text renewal instructions belong in Special Terms, which travels with the contract.
- Account Exec. Ownership is the single Contract Owner lookup; a contract carries no second ownership field.
Contract Document is a link, not an upload — it holds a URL pointing at wherever the document lives. The executed PDF itself goes on the record as an attachment; files are enabled on Contract, which is what makes that possible. There is no separate Order Form field.
Billing frequency
How often the customer is invoiced — four values, and a new contract starts on Monthly unless you change it:
- Monthly (default)
- Quarterly
- Annually
- One-time
There is no Custom billing frequency. A cadence that is none of these four has to be written out in Special Terms; the picklist will not hold it.
Payment terms
When the customer pays — five values, the same five a quote offers, so the two records speak one vocabulary:
- Net 15 — within 15 days of invoice
- Net 30 (default) — within 30 days
- Net 60
- Net 90
- Due on Receipt — immediately
There is no Net 45 and no Prepaid. Payment upfront is an arrangement you record in Special Terms, not a term you can pick here.
Renewals
Two fields drive renewals:
- Auto Renewal — off unless you turn it on. With it on, the daily renewal sweep opens the renewal deal for you; with it off, the sweep still reminds you but creates no deal.
- Renewal Notice (Days) — how many days before the end date the reminder fires. Defaults to 30, and each contract carries its own.
There is no Renewal Terms field. Instructions like "renew at 5% uplift unless the customer opts out" go in Special Terms, the free-text field on the contract itself.
When a contract enters its renewal window, the system files a follow-up task and emails the contract owner, on a deployment that runs scheduled work — see Daily — renewal reminder below for everything it creates.
What the system does automatically
On contract activation
Changing a contract's status to Activated runs two writes, and only these two:
- Signed Date stamped on the contract — today's date, and only when the contract has none yet. Set Signed Date in the same edit that activates the contract and your date is kept.
- Account type → Customer — every account that is not already a Customer is promoted, so this catches a Partner or a Former Customer just as it catches a Prospect.
Nothing is sent and nobody is told. Activation raises no notification for the contract owner or the account owner, and no message goes to the primary contact — there is no outbound customer email here, and no setting behind one. There is no Customer Since field on the account either: an account keeps no date of its own for when it became a customer, so cohort reporting has to read the contract's signed date or start date.
Both writes run after the save, with failures logged rather than raised, so the contract stays activated even if promoting the account fails. They also hang off an update — a contract that arrives already Activated (a data import, seed data, an integration that writes the record in one shot) triggers neither, and its account stays a Prospect.
Daily — expiration sweep
This sweep and the renewal reminder below are scheduled flows, so they run only on a deployment that has switched scheduled work on (see Administration › Automation). A default install has scheduled work off: there, no contract expires on its own, and an Activated contract past its end date stays Activated until someone changes its status.
Where it runs, each night at midnight the system finds Activated contracts whose end date has passed, and does two things to each — only these two:
- Status → Expired.
- Notification to the contract owner — inbox and email, linking to the contract.
Nobody else is told. There is no second notification: the account owner, the primary contact and the rest of the account team hear nothing when a contract lapses, so if anyone beyond the contract owner needs to know, an admin has to add that. The sweep also reads Activated contracts only — one still sitting in Draft or In Approval past its end date is left alone — and it takes at most 500 contracts per run.
Daily — renewal reminder
Where scheduled work runs (see above), at 8 AM the system finds Activated contracts whose end date falls inside that contract's own Renewal Notice (Days) window, and for each one, in this order:
- Files a renewal task for the contract owner — "Renewal due: contract CTR-0042", a high-priority follow-up due on the contract's end date, filed against the account.
- Notifies the contract owner — inbox and email. The notification links to the contract record, and that is all it does: there is no one-click action on it, and opening it creates nothing.
- Opens a renewal opportunity — but only when Auto Renewal is on. An Existing Customer - Renewal deal on the same account, at Proposal stage, amount copied from the contract value, close date set to the contract's end date, owner copied from the contract, next step "Confirm renewal terms with customer". It is skipped when that account already has an open renewal deal.
So the renewal deal is never something you click for. With Auto Renewal on it is already in the pipeline by the time you read the reminder; with it off, nobody opens one for you and the task is the whole of it.
The task is also what keeps the sweep from repeating itself: the same contract matches every morning of its notice window, and an open Renewal due task for it means the day is already handled — no second task, no second notification, no second deal. Complete that task and the next morning's sweep files a fresh one, for as long as the contract is still inside its window.
On a deployment without scheduled work none of this happens: no renewal task, no notification and no renewal deal. What still shows a renewal coming is the contract itself, through the Renewal Calendar tab and the End Date order of All Contracts (see Standard list views below).
Two bounds worth knowing: the sweep looks 120 days ahead at most, so a notice window set longer than that only starts firing at 120 days out; and it takes at most 500 contracts per run.
Manual termination
Terminating a contract is a status change to Terminated, and two things commonly assumed about that change do not hold.
Being the owner is not what qualifies you to make it. Edit on Contract is an object-level right that ownership does not extend: Sales Manager and System Administrator hold it, Sales Rep does not. That gap lands on the most common case, because the contract an accepted quote drafts carries the quote's owner across with it — so the rep whose deal it was ends up owning a contract they can read and cannot touch. Terminating it is someone else's action to take — a Sales Manager's, on any edition, since a manager can edit every account and therefore every contract; see Who can edit which contract under Sharing below.
And nothing holds the door shut afterwards. Terminated is declared a dead end, but on the same warning-severity status machine as the rest of the progression: an edit that moves a terminated contract back to Activated is written to the server log and saved anyway — see The status progression is advice, not a gate below. Termination is one-way by convention, which is what the status means and how everything downstream reads it, not by enforcement. If it has to be enforced, an admin has to author that.
Status is one of the two fields Contract keeps history on, so the audit log does record who moved it and when — a revival included.
Built-in rules
Three rules reject a save outright:
- End date must be after start date — the one validation rule on the object that refuses a contract.
- The term must match the dates — the contract hook counts the months between Start Date and End Date and rejects anything more than one month away from Contract Term (Months): "Contract term (12 months) does not match date range (6 months from … to …)." Term and dates move together, or the save bounces.
- An activated contract's end date cannot be pulled in — once the contract is Activated, an edit that moves End Date earlier is refused ("Cannot shrink end_date … after activation"). Pushing it out is fine; shortening a live agreement is a termination or an amendment, not an edit.
Two more things are often read as rules and are not:
- Contract Value cannot be negative — that is the field's own minimum of 0 rather than a validation rule, so NDAs at $0 are fine. Contract Term (Months) is bounded the same way, at a minimum of 1.
- The status progression is advice, not a gate. The declared route is Draft → In Approval → Activated → Expired or Terminated, with In Approval able to go back to Draft, Terminated reachable from all three live statuses, and Expired and Terminated both dead ends. But it is declared at warning severity: an illegal jump — Draft straight to Activated, or reviving an Expired contract — is written to the server log and the save still goes through. Nothing in the form stops it and nobody is notified. A contract created directly as Activated is not checked at all, which is the same import path that skips the activation writes described above.
And one rule people expect that is only half there: nothing stops an activated contract from being deleted directly. Contract has no delete hook and no status check of its own — who may delete a contract is decided by their profile alone, and the profile never looks at the status. A System Administrator can delete any contract, activated ones included; Sales Manager and Sales Rep can delete none, not even a draft. What is refused is deleting the account out from under its contracts. Contracts belong to their account (master-detail, so an account delete would cascade them), and the account guard refuses to delete a customer account while an Activated contract still references it — "Cannot delete customer account: 1 activated contract still references it. Terminate or reassign it first." An account carrying only drafts or ended contracts is refused too, by the platform rather than the guard: the delete cascades the account's contacts first and stops on the contract's required Primary Contact ("This Contact is still referenced by 1 Contract record(s) through Primary Contact, which is required and cannot be cleared"). In practice an account with contracts cannot be deleted until its contracts are gone. If signed agreements must be undeletable outright in your org, an admin has to author that — the app does not do it for you.
Sharing
Contracts are Controlled by Parent under the account: you see the contracts of every account you can see, by whichever route the account reached you — you own it, a territory or team sharing rule granted it, or someone shared it with you by hand. No sharing rule is authored on Contract itself, and none is needed; the account's rules carry its contracts (see Sharing & Security, Controlled by Parent, in practice). A rep who reached the account through a territory rule therefore sees its Contracts related list complete, including the renewals — the gap that #549 reported.
Reading is one door; editing is two:
- Object level first. Edit on Contract is a right on your profile that record visibility never extends: Sales Manager and System Administrator hold it, Sales Rep does not. A rep reads every contract on the accounts they can see — the one drafted from their own accepted quote included — and cannot change any of them.
- Then the account. Changing, adding or removing a contract requires edit access to its account, resolved the same way as reads. Ownership of the contract row itself is irrelevant.
Who can edit which contract
| Profile | Can edit… | Why |
|---|---|---|
| System Administrator | any contract | Modify All on Contract — the super-user bypass |
| Sales Manager | any contract | holds Modify All on Account, so every account — and every contract under it — is editable; deleting a contract stays refused (no delete right on Contract) |
| Sales Rep | none | no object-level edit right at all — the first door is shut |
That middle row is the common case, and it holds on every edition: the contract an accepted quote drafts belongs to the rep, and the manager changes it because they can edit its account, not because of any reporting relationship. (Earlier releases declared a hierarchy write depth here that only the Enterprise edition could resolve, so on the open edition a manager reached only their own contracts; that boundary is gone with the parent-derived model.)
Standard list views
Contracts saves one list — All Contracts, every contract you may see, with no filter on it — and the tab row along the top of that list changes how the same records are drawn, not which records are in them. Four tabs, and nothing else:
| Tab | How it draws the same contracts |
|---|---|
| All Contracts (the landing tab) | The grid: contract number (pinned left, and the link into the record), account, related opportunity, status, contract value with a column total, term in months, start and end date, auto-renewal and owner. Ordered by End Date, soonest first, so whatever expires next is on top. 25 rows a page. |
| Renewal Calendar | The same contracts on a month calendar, each placed on its End Date, titled by contract number and coloured by status. |
| Contract Terms | One gantt bar per contract, from start date to end date, so overlapping terms line up against one another. |
| Contract Timeline | The same start-to-end spans on a quarter scale, grouped by account and coloured by status. |
None of the four filters anything, so every contract you can see appears in all four and switching tabs only redraws them. There is no saved My Active Contracts, Expiring in 60 Days, Up for Renewal, Expired This Month or Pending Activation — to narrow the list to your own contracts or to one status, sort and filter the grid columns yourself. What watches renewal dates for you, where scheduled work runs, is the daily renewal reminder described above, not a list view.
Tips for contract owners
- ✅ Attach the signed PDF when activating — auditors will thank you.
- ✅ Set Renewal Notice (Days) = 90 for enterprise deals — gives you a full quarter to negotiate, and the sweep looks far enough ahead to honour it.
- ✅ Record what you agreed in Special Terms — "price-locked for 3 years; 5% uplift on renewal". There is no Renewal Terms field, and Special Terms is the note that will still be on the contract when the reminder lands.
Tips for sales reps
- ✅ You never fill a contract in — accepting the quote drafts it. Contract is read-only for a Sales Rep: you cannot create one and you cannot edit one. What you do own is the quote, and marking it Accepted is what drafts the contract — a Draft contract carrying the quote's account, primary contact, related opportunity, owner and total, on a 12-month term starting today, typed Subscription.
- ✅ The opportunity link is copied off the quote, not pre-filled on a form you are typing into — so there is nothing on it for you to keep or lose. Nor does the manual route pre-fill it: when a manager or an admin creates a contract by hand, the opportunity is picked by hand, and the only help the picker gives is that it lists the deals of the account already chosen on the contract. Nothing reads that link for reporting today — HotCRM ships no contract report or dashboard at all.
- ✅ You read the contracts of your accounts and ask for the rest. Contracts follow the account: every account you can see — your own, and the ones a territory or team rule shares with you — shows you its contracts, renewals included. Changing a term, a date, the value or the status is somebody else's job, and so is an amendment — that is a new contract, which only they can create. Ask your Sales Manager: a manager can change the contract standing in your name without it being handed over first, on any edition — see Who can edit which contract above.
Tips for admins
- Contract activation is an object hook (
src/revenue/objects/contract.hook.ts), not a flow — that is where the signed-date stamp and the account promotion live, and it is not in the flow list. The expiration sweep and the renewal reminder are scheduled flows, so they run only once the deployment switches scheduled work on. See Administration › Automation. - Contract types and billing frequency picklists are extensible.