Construction contracts that build themselves from the estimate

When a customer approves an estimate and fills in their details, the platform writes their construction agreement — priced, dated, and ready to sign — instead of you retyping a Word template.

What it is

A contract row is created the moment the customer submits the intake form. The org's active template body is rendered with the customer's legal name, mailing address, job site, the approved estimate's line items and payment schedule, and the governing-law notice for their state, then frozen on the row as `rendered_body`. The customer signs it in their portal; the builder counter-signs; both signatures, timestamps, IP and user agent are stored on the same row.

Also called: project agreement · construction agreement · builder contract · customer contract · e-signature contract

See it
Construction contracts that build themselves from the estimate
Prepared for
Sutter Kline
1140 Ridge Line Rd · Fair Play
Shell package$32,451
Concrete & site$9,421
Doors & windows$6,281
Trim & finish$4,187
Total
$52,340
Signature
Split screen: the approved estimate on the left, the generated agreement on the right with the customer's name, address, total and payment schedule highlighted as filled-in. Rendered by src/components/contracts/ContractMarkdown.tsx inside ContractViewModal. Sample data — no customer information appears here.
How it works
  1. 1Customer approves the estimate in the portal.
  2. 2Customer completes the Customer Information Form (only the sections the builder switched on).
  3. 3Submit creates a contracts row: the active template is rendered with {{tokens}} and frozen so later template edits never rewrite this customer's copy.
  4. 4Builder reviews it in Review & Send, optionally edits it for this one customer, and releases it.
  5. 5Customer types their legal name, draws a signature, and (on waiver templates) initials each numbered clause.
  6. 6Builder counter-signs; status flips to fully_executed and the signed copy is filed to the customer's Documents.
Why we built it

The whole pipeline exists because the paperwork step was the one place a sold job could sit for a week. The lib header states the design rule that everything else follows: the rendered body is 'frozen as `rendered_body` so future edits to the template don't rewrite this customer's contract.' Contract ids are the only thing protecting the document — the submit route switched them to `crypto.randomUUID` with the note that makeId 'only gives ~26 bits of random entropy... Using crypto.randomUUID gives 122 bits of cryptographic randomness, making brute-force exhaustion permanently impractical regardless of rate-limit posture.'

The problem
  • Retyping customer name, address, price and payment terms into a template for every job
  • Contracts that quietly disagree with the estimate the customer approved
  • No record of when, from where, or by whom a contract was signed
  • Template edits silently changing what an already-signed customer agreed to
Sound familiar?
What you get
Contract exists minutes after the estimate is approved, not days
Every signed copy is frozen evidence — template changes can't rewrite history
Both signatures, both timestamps, IP and browser stored on one row
Signed agreement lands in the customer's Documents automatically

See it on your own jobs

Twenty minutes, your numbers, no slide deck. We’ll build one of your real buildings in front of you and send you the estimate link at the end — yours to keep either way.

or keep browsing features →