The Contract object
A contract records what was agreed with a customer and when it runs out. A quote is an offer and a deal is a negotiation; neither carries a term, which is why neither can tell you a renewal is coming.
The Contracts API is read-only. Contracts are created and moved in the app or through the AI connector.
| Field | Type | Notes |
|---|---|---|
id | string | CUID (read-only) |
number | integer | Per-workspace contract number, shown to people as #12 |
title | string | What the contract is |
status | string | One of DRAFT, ACTIVE, EXPIRED, CANCELLED |
value | number | null | Contract value. null when no value was recorded |
startsAt | string (ISO 8601) | null | Start of the term |
endsAt | string (ISO 8601) | null | End of the term, and the date the renewal alert fires against |
autoRenew | boolean | Whether the term rolls over on its own |
renewalNotifiedAt | string (ISO 8601) | null | When the renewal alert went out, or null if it has not |
notes | string | null | Free-form notes about the agreement |
company | object | null | The account holding it: { id, name } |
deal | object | null | The deal it came out of: { id, name } |
owner | object | null | The rep who owns it: { id, name }, or null when unowned |
createdAt | string (ISO 8601) | Assigned on create (read-only) |
updatedAt | string (ISO 8601) | Updated on every write (read-only) |
Example object
{
"id": "cmq2cont0alk34567890",
"number": 12,
"title": "Acme master agreement",
"status": "ACTIVE",
"value": 12500.5,
"startsAt": "2026-01-01T00:00:00.000Z",
"endsAt": "2026-12-31T00:00:00.000Z",
"autoRenew": true,
"renewalNotifiedAt": null,
"notes": "Priced against the 2026 catalog.",
"company": { "id": "cmq2comp0alk34567890", "name": "Acme Corp" },
"deal": { "id": "cmq2deal0alk34567890", "name": "Acme 2026 renewal" },
"owner": { "id": "cmq9user0alk34567890", "name": "Kevin Rep" },
"createdAt": "2025-12-14T12:00:00.000Z",
"updatedAt": "2026-01-02T09:31:00.000Z"
}Notes on specific fields
value is a decimal column in the database and is serialized as a plain JSON number. A contract with no value recorded returns null, not 0, so you can tell "nothing was agreed on price" apart from "the price was zero".
endsAt is what makes renewal possible at all. Thirty days before it, an ACTIVE contract raises a renewal alert for its owner, once, and renewalNotifiedAt is stamped so the alert cannot repeat. Moving endsAt clears that stamp, because a renewed term deserves its own warning.
Renewal is not a second object. Nothing creates a follow-on contract automatically. A renewal is somebody moving the end date, or writing a new contract, and both are decisions a person makes.
autoRenew changes what the alert should say rather than what it does. An auto-renewing term still raises the warning, because "this is about to roll over for another year" is exactly the thing somebody wants to know in time to stop it.