Contract generated from the approved estimate

One shared generator turns the estimate snapshot plus the customer's intake answers into a finished contract body — and the same code runs whether it's the first render or a later re-render.

What it is

BuildContractArtifacts / buildContractContext assemble the substitution context (builder branding from invoice_settings, customer from form_data, project from the estimate snapshot, money from the snapshot and job_meta) and return both the rendered body and the frozen required-sections manifest. Three callers use it: first generation on form submit, template switching, and pulling a contract to the live estimate.

Also called: auto-generate contract · contract from estimate · estimate to contract · no retyping

See it
Contract generated from the approved estimate
Prepared for
Marlin Hoover
1140 Ridge Line Rd · Fair Play
Shell package$36,530
Concrete & site$10,606
Doors & windows$7,070
Trim & finish$4,714
Total
$58,920
Signature
A flow strip: estimate snapshot + intake form + template → one generator → rendered contract, with the same generator badge shown on the 'switch template' and 'pull to live' arrows. lib/contractGeneration.ts. Sample data — no customer information appears here.
How it works
  1. 1Load the org's active contract template (seeding the default if none exists, so the tenant's contracts share one template id for audit).
  2. 2Pull builder branding from invoice_settings.
  3. 3Compose the context: dates, customer identity, project address and schedule, estimate totals, payment schedule, line-item table, governing-law state and notice.
  4. 4renderTemplate() substitutes the tokens; extractRequiredSections() derives the initials manifest from numbered headings.
  5. 5Freeze rendered_body + required_sections on the contracts row.
Why we built it

The shared-generator design is a direct response to drift between render paths: 'Keeping this in one place means the per-contract template picker produces byte-identical output to the original generation — same estimate table, payment schedule, governing-law clause, and initials manifest.' The alternative — a second, slightly different renderer for re-renders — is how two copies of the same contract end up disagreeing.

The problem
  • Divergence between the estimate the customer approved and the contract they sign
  • Multiple render paths producing subtly different documents
Sound familiar?
What you get
One generator, one output, every time
Estimate numbers, schedule and clauses arrive in the contract without typing
Re-rendering is safe because it produces exactly what a fresh submission would
What's inside
What they signed is frozen forever
The exact text the customer read is stored on their contract row, so editing your template later can never rewrite a document somebody already signed.
Your real payment terms in the contract
The contract states the payment stages set on that job — percentages and dollar amounts — instead of a generic 50/50 the software invented.
The estimate's line items printed into the contract
{{estimate.line_items_table}} renders the approved estimate's material sections as a real table inside the contract, with subtotal, tax and total beneath it.
State lien notice picked from the job site
The contract's governing-law state and its statutory notice derive from the property address — Missouri gets the mechanic's-lien warning, Kansas gets the 90-day defect notice, everywhere else gets a neutral clause.
Margin can't fall out of the contract total
When the contract has to compute a total from parts, markup is included — a fix for a path that once produced a contract priced at cost.
Contract links a customer can open without a password
The customer signs from a link — no account, no password — and the link is a 122-bit random id, rate-limited on read.

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 →