The Cash Seam Build Standard
How the job written on the call becomes the quote, the invoice and the chase without anybody retyping it, and without a second record appearing anywhere along the way. Written so you can build it yourself.
Used by
- Operations Manager
- Office Manager
- Bookkeeper
- Business Systems Analyst
9 min read · Published by AI Pathway
Using Claude? This standard installs as a skill. Guides for ServiceM8, Xero and Simpro.
How to get this live in your business
Build it for mePick a time and we trace one real job from your phone line to your ledger, and show you where the id dies.What this is
An open build standard for the gaps between four others. The call writes a job, the quote is drafted from it, the invoice is raised from the accepted quote, and the chase reads that invoice off the ledger. This standard checks that one record survives every one of those moves.
The core rule
An id is the number the job system or the ledger gives a record, like job 1042. Each step carries the id of the record before it. Nothing downstream looks a customer up by name or phone to find out which job it is working on.
What it does not do
It never makes a second job for a caller who rang twice, never treats a chat window saying done as a record, and never needs somebody to have a chat session open for the next step to happen.
The hard part
Not any one step. The write that returns no id and looks like it worked, and the second call that looks like a new customer. Identity from the system of record, and the single authenticated write, are the steps not to hand-roll.
Mrs Lee rang on Monday night and again on Tuesday morning, from the same number, about the same leaking tap. The office now has two jobs, a quote on one of them, and an invoice raised against the other, because the bookkeeper found it by her name. The chase went out on the invoice she had already paid. Every step did its own job properly. The money went missing in the gaps between them.
One record, four moves
Booked After Hours
The call writes one job, or attaches to the open one.
- job_id
- duplicate_of
writes the job
Quote Out
Woken by job created. Reads the job by its id.
- quote_id
- job_id
drafts from it
Invoice Out
Woken by job completed. Raises from the accepted quote.
- invoice_id
- quote_id
- job_id
raises from it
Debtor Chasing
Woken by invoice overdue. Reads the invoice off the live ledger.
- invoice_id
chases it
One handover, the same at every hop
- A named event wakes the hop: job created, quote accepted, job completed or invoice overdue, by webhook, schedule or polling. A chat message is not a trigger.
- Read the record for an open exception first. If one is open, the hop stops and the hold names that exception's owner.
- Read the upstream record by the id the event carried, from the job system or the ledger. Never look it up by a customer name or a phone number.
- Write the next record carrying that id: the quote carries the job id, the invoice carries the quote id and the job id, the chase carries the invoice id it read off the ledger.
- Did the system return an external id, and does the record read back by it? If no, it was not written: an exception is raised with the owner the business set for that stage, and nothing downstream acts on it.
- If yes, the next hop is woken later by its own named event, not by this one.
- Is the send released by a named person or a named rule? If yes, it is sent, and the id it carries is the id it read. If no, it waits for a person.
Take the spec to Claude, ChatGPT or Copilot. The sentence to paste is one click away.
1. What this standard covers
Any business that takes an enquiry, quotes it, does the work, bills it and chases it, with a job system and a ledger underneath. If you are building any two of the four standards below, this is the one that makes them one system instead of four.
- Booked After Hours writes the job on the call.
- Quote Out drafts a quote from that job, priced only from the book.
- Invoice Out raises the invoice from the accepted quote or the completed job.
- Debtor Chasing chases off the live ledger.
Each of those passes on its own, and none of them fails if the id dies between two of them. That is the gap. This standard restates nothing the four already say. Every check below is about what crosses from one to the next: the id, the event that wakes the next step, and the exception with a name on it.
What cannot meet it. A job system with no API cannot hand its job id to anything, so the chain cannot start from it. Tradify is the common case: a Tradify business can run Quote Out, Invoice Out and Debtor Chasing from the ledger alone, with the job entered by a person, but not the seam from the call. The standard says so rather than claiming a link that does not exist.
2. The objects, and what is in them
Five objects, all of them already in the job system or the ledger. The seam adds no table of its own: it adds one field to each record, the id of the record before it, and an owner on every exception.
For your developer. Show the fields
Enquiry # one per call or form
- id, phone, door (phone | web)
- duplicate_of (job_id, when it attached to an open job)
Job # the job system's record
- id (issued by the job system; the only key anything downstream uses)
- customer, phone, status (open | complete)
- enquiry_ids[]
- trigger (the event that woke the write)
- exception { reason, owner } (nullable)
Quote # the job system's record
- id (issued by the job system)
- job_id # the upstream id, never null
- status (draft | released | sent | accepted)
Invoice # the ledger's record
- id (issued by the ledger; null means the write did not happen)
- quote_id (the accepted quote, where there is one)
- job_id # always
- status (draft | released | sent), due_at
Chase # a send, not a record of its own
- invoice_id # read off the ledger in the same run
- released_by (person) | released_by_rule (rule name)
- triggerA Quote with no job_id, an Invoice whose job_id came from a name search, and a Chase whose invoice_id was not in the ledger read before it are the three defects this standard makes visible. None of them is wrong inside its own standard. All three are wrong at the seam.
3. The checks, in order
The order is the claim. Identity comes first because every other check is about carrying it. The null id comes before the chain because a chain that carries a null forward is worse than one that stops. The trigger and the owner come last because they are what keeps the chain moving when nobody is watching, and what stops it safely when something goes wrong.
- Every downstream step takes its id from the system of record, never from a name or a phone number. Once a job exists, every later step reads the upstream record's id from the job system or the ledger: from the event that woke it, or from a read of that record by id. It never mints its own key and never matches on a customer name or a phone number.
- One job per enquiry: a repeat call or a second door attaches to the open job. Before writing a job, the build reads for an open job on that customer or number. A repeat call, or the same customer arriving through a second door, attaches to the open job with duplicate_of set. A second job is a fail.
- A write that comes back without an external id is treated as not written. A write into the job system or the ledger that returns a null or missing external_id did not happen, as far as the chain is concerned. The build escalates it and nothing downstream acts on it: no quote from that job, no send of that quote, no chase of that invoice.
- The quote carries the job id, the invoice carries the quote or job id, and the chase reads the invoice id off the live ledger. Each record carries the id of the one before it. The quote names the job. The invoice names the accepted quote and the job. The chase names an invoice id the ledger returned when the build read it. A break anywhere in the chain is a fail.
- A chat or connector confirmation is not a write and not a release. A write counts only when it is read back through the port from the system of record. Every send to the customer, the quote, the invoice and the chase, waits for a release that names a person or a named rule. The model saying it is done is neither.
- Every hop starts from a named event, and none waits for a chat session. Each hop is woken by a named event arriving by webhook, schedule or polling: job created, quote accepted, job completed, invoice overdue. A build that only moves while somebody has a chat window open fails, and so does one that acts on a chat message as if it were an event.
- An exception names its owner, travels with the record, and the next step does not pass it. A parked or escalated item names the person who owns it, taken from the owners the business set for that stage. The exception stays on the record, and the next step reads it and stops rather than proceeding past it.
- It survives the builder. Someone other than the builder can explain what it does, and it runs on an account the business owns.
- The pass test passes. On the business's own job system and ledger: the same caller rings twice, the quote is accepted, the job completes and the invoice goes overdue. Exactly one job, one quote, one invoice and one chase exist, each carrying the upstream id. A null external id injected anywhere stops the chain.
4. The three handovers
| From | To | Carries | Woken by | What it stops |
|---|---|---|---|---|
| The call | Quote Out | job_id, as the job system issued it | job created | A quote keyed to the caller's phone number, or a second job because they rang back |
| Quote Out | Invoice Out | quote_id of the accepted quote, and the job_id | job completed (or quote accepted) | An invoice matched to the job by the customer's name, with the wrong quote's scope on it |
| Invoice Out | Debtor Chasing | invoice_id, read off the live ledger | invoice overdue | A chase run from last week's export, for an invoice that was paid on Tuesday |
The phone number is allowed in exactly one place: at intake, to find an open job before writing a new one. After that, it is never a key. Two customers share a name more often than anyone expects, and one customer changes their number more often than that.
Every hop is woken by a named event, by webhook, schedule or polling. A chat window is a fine place to ask a question. It is not a trigger, and a build that only moves while somebody has one open stops on the first weekend.
5. When a write comes back empty
A write into a job system or a ledger can return a success code and no id: a timeout after the record was made, an empty body, a rate limit halfway through a batch. A connector can report that it created a quote before the quote exists. In both cases the honest answer is that nothing was written, so the build escalates and the chain stops there.
The test is a read through the port. A record counts once the job system or the ledger returns it by its id. Until then there is nothing to quote from, nothing to send and nothing to chase, and an exception is on the record with the name of the person who owns it. The next step reads that exception and does not go past it.
6. What stays with a person
- Every exception, by the owner the business named for that stage: the job, the quote, the invoice, the chase.
- The release of every send, or the rule a person set that stands in for it.
- Deciding two enquiries are the same job when the number does not say so.
- Everything the four standards it binds already keep with a person: prices, variations, disputes and hardship.
The parts that move
Checked as at 3 October 2026
Tradify has no public or partner API, confirmed in writing by their support on 10 April 2026. If that changes, the scope in section 1 changes with it.
Each hop’s hosted step is live on its own page. No end-to-end run of the seam on a real tenant has been published yet, so the provider block below says stub by default.
Check these against the primary instrument before you rely on them, and store the date you checked beside the value rather than hard-coding it. This standard tells you the shape and where to look. It is not legal advice and it does not tell you that anything complies.
8. Provider: what to stub, and what not to hand-roll
One step not to hand-roll, and it is the same step at every hop: identity from the system of record, and the authenticated single write into the job system or the ledger that returns its id. Everything else is wiring a coding agent builds against the stub in an afternoon.
For your developer. Show the provider config
provider:
stub: local.mock_cash_path
production: office_voice.recover # Office Voice / Recover, sold as Get Paid
default: stub # no live mapping in the conformance kit yet
the live steps # each named on its own standard
- the job write and the quote draft: Office Voice Front Desk, sold as
Booked After Hours
- the invoice draft and the chase: Office Voice Recover, sold as
Get Paid
stays local # yours, and the standard
- the open-job read at intake, and duplicate_of
- carrying each id to the next hop
- the triggers: webhook, schedule or polling
- the exception owners per stage, and the release rule
cannot meet the standard
- a job system with no API, for the first hop. Ledger-only is the
honest route there.A vendor selling an end-to-end cash flow is usually selling four screens that each look right. Ask them one thing: when a customer rings twice and pays late, which id is on the chase, and where did it come from?
The pass test. On the business’s own job system and ledger, not a sample. Ring the line twice from the same number. Accept the quote that comes out. Mark the job complete. Let the invoice go past its due date. There must be exactly one job, one quote, one invoice and one chase, and each must carry the id of the one before it as the job system or the ledger issued it: the second call attached to the first job, the quote naming the job, the invoice naming the quote and the job, the chase naming the invoice it read off the ledger. Then run it again and force one write to come back with no external id. The chain must stop there, with an escalation naming its owner and nothing released, sent or chased downstream of it. A stub cannot pass this: it issues whatever ids it likes and links whatever it is told to, so a build can agree with itself there and still key the real invoice to a name.
9. Conformance checklist
Hold a DIY build or a vendor to this. If a box is empty, it is not in production.
- 1. Every downstream step takes its id from the system of record, never from a name or a phone number. Once a job exists, every later step reads the upstream record's id from the job system or the ledger: from the event that woke it, or from a read of that record by id. It never mints its own key and never matches on a customer name or a phone number.
- 2. One job per enquiry: a repeat call or a second door attaches to the open job. Before writing a job, the build reads for an open job on that customer or number. A repeat call, or the same customer arriving through a second door, attaches to the open job with duplicate_of set. A second job is a fail.
- 3. A write that comes back without an external id is treated as not written. A write into the job system or the ledger that returns a null or missing external_id did not happen, as far as the chain is concerned. The build escalates it and nothing downstream acts on it: no quote from that job, no send of that quote, no chase of that invoice.
- 4. The quote carries the job id, the invoice carries the quote or job id, and the chase reads the invoice id off the live ledger. Each record carries the id of the one before it. The quote names the job. The invoice names the accepted quote and the job. The chase names an invoice id the ledger returned when the build read it. A break anywhere in the chain is a fail.
- 5. A chat or connector confirmation is not a write and not a release. A write counts only when it is read back through the port from the system of record. Every send to the customer, the quote, the invoice and the chase, waits for a release that names a person or a named rule. The model saying it is done is neither.
- 6. Every hop starts from a named event, and none waits for a chat session. Each hop is woken by a named event arriving by webhook, schedule or polling: job created, quote accepted, job completed, invoice overdue. A build that only moves while somebody has a chat window open fails, and so does one that acts on a chat message as if it were an event.
- 7. An exception names its owner, travels with the record, and the next step does not pass it. A parked or escalated item names the person who owns it, taken from the owners the business set for that stage. The exception stays on the record, and the next step reads it and stops rather than proceeding past it.
- 8. It survives the builder. Someone other than the builder can explain what it does, and it runs on an account the business owns.
- 9. The pass test passes. On the business's own job system and ledger: the same caller rings twice, the quote is accepted, the job completes and the invoice goes overdue. Exactly one job, one quote, one invoice and one chase exist, each carrying the upstream id. A null external id injected anywhere stops the chain.
- 1
Hold the build to the checklist
The checks in this standard are what a working build looks like. Implement it yourself, hand it to a developer, or hand the page to an AI with the button below.
- 2
Do not hand-roll
There is no product for this step. Highest option: none. Checklist plus a build.
- 3
If you are stuck
Exceptions, three systems, a team that will not maintain a Zap, or an owner who wants it done. We implement when it has to survive the business.
Related reading
- Combining the build standards: the knowledge stack, and the cash path in stack order
- The Booked After Hours Build Standard: where the job id is born
- The Quote Out Build Standard: the quote drafted from that job
- The Invoice Out Build Standard: the invoice raised from the accepted quote
- The Debtor Chasing Build Standard: the chase off the live ledger
- The Two Records Build Standard: when the job system and the ledger disagree anyway
Want the whole thing by email?
This paper, plus the rest of the library. One click to stop them.