Two paths. /api/contracts/[id]/send-copy emails a tenant-branded message whose subject and template vary by state: 'Your fully-signed agreement for …' (contract.fullySigned), 'Your signed agreement for …', or 'Your project agreement for …' (contract.signedCopy). Separately, sendContractToCustomer drops a contact_documents row titled 'Project Agreement — Fully Executed (<date>)' pointing at the portal URL, and refuses to run before both parties have signed.
Also called: resend signed contract · email the signed agreement · customer lost the contract
- 1Role gate: admin, sales or project_manager, with the org check returning the same 'not found' shape as a genuine miss so cross-tenant existence never leaks.
- 2The recipient email comes from the estimate link; an invalid address returns a specific instruction to update the contact.
- 3The document row resolves the contact via the contract, falling back to the estimate link, and returns a named reason when no contact is paired.
- 4A missing email channel returns actionable guidance rather than a generic failure.
The library function is explicit that it records the paper trail rather than owning delivery: 'this just records the paper trail… primarily for re-sends + an admin-discoverable "send" surface.' Its failure handling is deliberate too: 'any failure short-circuits to a `{ ok: false, reason }` so the UI can surface why the send didn't land (no contact pairing, etc.) instead of a silent fail.' The fully-executed gate exists 'so a draft / customer-signed-only doc can't accidentally ship before the builder's signature is on it.'
- Manually attaching a PDF to an email for every re-send
- Half-executed documents being sent as final
- Silent send failures with no explanation
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 →