Research, design and ship, inside a week.
Contractors — roofing, HVAC, plumbing, solar. Dispatchers and office staff at a desk, technicians on a phone in the field.
Jobs, scheduling and dispatch, crews and skills, parts and inventory, quotes and invoices, customer records.
The job. Everything about a piece of work hangs off one job record, and that's where our users spend their day.
A contract before the work. A waiver during it. A completion certificate after.
All of it was written in a PDF editor, emailed, chased, signed somewhere else, and filed wherever the person who sent it happened to file it.
So the one document that proves the work was agreed to sat outside the system that ran the work.
Nobody could answer “did the customer sign it?” without leaving Zuper and asking a person.
We didn't want to spend a week solving a phantom problem, so we went and checked it from four directions first.
Live calls told us what to look for. Searching the recorded ones told us how often it came up.
They were already paying a separate e-signature subscription for the last step of a job that lived in Zuper.
Communities over-represent the confident and the annoyed. So anything we found there had to also show up in a call or in the data before it counted.
It's also where the vocabulary came from. They say paperwork, not documents. That word ended up in the UI.
Their own contract, unchanged, filled in by our job record.
Document tools are powerful and know nothing about the job. Field-service platforms know everything about the job and treat paperwork as an attachment you drag in.
Real problem, and they were already paying someone else to solve it.
A new module would have been easier to design and wrong — the paperwork only makes sense attached to the job it came from.
| Model | Result | Why |
|---|---|---|
| Templates locked, no job-level edits | rejected | Safe and unusable. Every job needs something changed, so they go back to the PDF editor. |
| Anyone can edit anything on a job | rejected | A salesperson can delete a liability clause by accident and the document still looks fine. |
| Our services team builds each document | rejected | Doesn't scale. Their paperwork changes faster than our tickets close. |
| Template author locks it, page by page | shipped | The person who knows why a clause exists is the one who protects it. The job gets a copy it can adapt but not damage. |
Walked them through it in words. Does “you lock the pages, your salesperson fills in the rest” match how you'd actually work?
Our own implementation and support people, who set these things up for customers every week and know where it breaks.
Is a per-page lock and a copy-on-apply model actually buildable in the Jobs module as it stands?
Validating a model is cheaper than validating a screen. If the model is wrong, no amount of interface fixes it.
“Job Template Type Update” — a template list, a create-document modal, and a logic flow.
Rough on purpose. It fixed the scope, not the design.
It named the screens he expected to exist and left how they work to me — the right level to hand over at.
It also carried one hard constraint: the Module field pre-filled with “Jobs”, locked, labelled RESTRICTED V1. Documents belong to a job in v1 and nowhere else.
No Figma round-trip on this one. The system I maintain was the starting point.
Block editor, PDF upload, draggable fields on real coordinates.
Anyone could open it and click — engineering, SMEs, the PM, customers.
The build was fast because the research was already done — and because the design system already existed.
| The tension | Where it landed | Why |
|---|---|---|
| How much ships in v1 | Split into phases | Engineering bandwidth was the real limit. We cut by phase rather than shipping all of it thinly. |
| Rich text on written pages | Worked inside TinyMCE | The editor we already run couldn't do everything we wanted. The design bent to it rather than blocking the release. |
| New builder or reuse ours | Repurposed the existing one | The proposal builder already did pages and blocks. Reusing it meant one experience to learn and one to maintain. |
Two of these three are constraints, not compromises — and saying which is which is most of the job.
Cheap to produce, because they were variations on a settled model rather than three different ideas.
They clicked the deployed prototype rather than looking at a picture, so the feedback was about behaviour — what happens if I edit after sending, who signs first, what if my PDF is a scan.
Those questions are much cheaper to answer before release than after.
Back to the same data we used to justify it. Are documents actually being created per job, and are they reaching signed rather than sitting in draft?
Then back to the customers who'd helped us shape it, to hear what the first real contracts through it felt like.
Customer calls with the PM, Fathom across the recorded ones, PostHog, the NoQuit community, competitors. Four checks, same answer.
Four models, one shipped — validated with customers, SMEs and engineering before any screen existed.
Existing tokens and components plus Claude Code, three variations, three reviews, back to the customer, out.
The build was fast because the research was slow. Not instead of it.
Today someone drags every field onto a contract by hand. The blanks are findable — a model can propose them and the author confirms.
We already know the job's customer, address and price. Mapping them to a blank is a suggestion problem, and the author still approves it.
Before a document goes out, show what's been edited away from the locked template. Catches the silent failure we designed around.
All three are suggestions a human confirms. Nothing auto-sends a legal document, and nothing edits a locked page.
Everything in this deck, running.
zuper-document-engine.vercel.app ↗Add a page. Type / for blocks. Upload a PDF and drop a field on it.