← All standards
Open specificationDraftPublished September 2026Changelog

The Quote Out Build Standard

An open specification for turning a job that already exists into a quote somebody can accept. Build it yourself, hand it to an AI coding agent, or hold whoever built yours to it.

Used by

  • Estimator
  • Office Administrator
  • Operations Manager

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 get the quotes out for you.
Build it myself

DIY. The objects, the checks in order, and the pass test.

What this covers

One job that already exists, turned into one quote in draft, in the system of record you already run, or parked with a reason.

The first rule

We do not render the quote. It is drafted into ServiceM8, Simpro or Xero against the price list already in there. Nothing we hold is the copy that counts.

The hard part

The rate. Every priced line traces to a catalogue id in the customer's own price list, read at draft time. Anything the book does not cover is parked, never estimated.

Write once

One job, one quote. The overnight email and the morning call are the same job, so they are the same quote, kept rather than duplicated.

What stays with a person

Sending. Also variations, discounts, and any line the price list does not cover. The standard prices the catalogue, not the negotiation.

The pass test

Section 9. One real job in a real tenant: once with a line the price list does not cover, then twice without it. A stubbed implementation cannot pass it.

The call was answered. The job is in the system. Then it sits there, because the person who can price it was on a roof all day, and by the time they are not, the customer has had two other quotes. Nothing in that sequence is a technology failure. The quote is late because drafting it is nobody’s next action.

One job, one quote

  1. A job already exists, and it is the only trigger. Never a raw call or email, whichever door the work arrived through.
  2. Read the price list live, at draft time. A cached copy older than the agreed window parks the quote rather than pricing it.
  3. Every priced line traces to a catalogue id in the customer's own price list. No rate is inferred, averaged, or recalled from another job.
  4. Variations to an accepted quote, and any discount, escalate to a person. The standard prices the catalogue, not the negotiation.
  5. Every line in the book? If no, the whole quote parks with a reason from a fixed list, with the priced lines attached. Parking is a real outcome, not a failure, and the unpriceable line is never estimated.
  6. If yes, draft it into ServiceM8, Simpro or Xero. Nothing we hold is the copy that counts.
  7. The step not to hand-roll: did their tenant return an id? If yes, the draft waits for a person to send, because this standard has no send port at all. If no, the null id raises an alert and the job comes back through the same checks, where one quote per job means it is updated rather than duplicated.
Everything left of the filled diamond runs on a laptop with no credentials. The diamond is the swap from the stub to a real tenant, which section 8 calls the step not to hand-roll, and the loop is the second run the pass test requires.

Take the spec to Claude, ChatGPT or Copilot. The sentence to paste is one click away.

1. Scope, and what is out of it

In scope: one job that already exists in the system of record, turned into one quote in a draft state in that same system, or parked with a reason. The job may have arrived by any door: an after-hours call, an overnight web form, an email. The standard does not care which, and section 6 is the reason it must not.

Out of scope: creating the job. That is the Booked After Hours Build Standard. Chasing the quote once it is sent is the Follow-Up Build Standard; collecting on the invoice that follows is the Debtor Chasing Build Standard. This standard is the join between them and nothing else.

Also out of scope, deliberately: pricing judgement. Variations to an accepted quote, discounts, and any line the price list does not cover are parked for a person. A build that estimates its way around a missing catalogue line is not a conforming build, it is a liability with a total on it.

Systems this can be met on: ServiceM8, Simpro and Xero all expose a write for a quote record. Our hosted step (section 8) writes the draft into Xero, priced by Xero from its Items, once the customer on the job is matched to a Xero contact. Without Xero connected, our connector cannot read a ServiceM8 or Simpro price list yet, so the quote parks with pricebook_unreadable. Simpro is parts-only either way, because prebuild sell prices are not in its API. Tradify cannot meet this standard: it has no public API, so there is no sanctioned route that creates a quote in it. That is the same position we take on the trades page about booking a job, and for the same reason.

2. The checks, in order

Order matters. The price list is read before anything is priced, and what cannot be priced is parked before anything is drafted, because a half-priced quote in a draft folder is worse than no quote: it looks finished.

  1. 1. A job is the only trigger

    The quote is drafted from an existing job record, never from a raw call or email. Both doors reach this standard through the same job.

  2. 2. The price list is the only source of a rate

    Every priced line traces to a line in the customer's own price list. No rate is inferred, averaged, or recalled from another job.

  3. 3. What cannot be priced is parked

    Work with no matching price-list line parks the quote with a reason. It is never estimated, and the quote is not drafted around it.

  4. 4. Variations and discounts stay with a person

    A variation to an accepted quote, and any discount, escalates rather than drafting. The standard prices the catalogue, not the negotiation.

  5. 5. One quote per job

    A job already carrying a draft or a sent quote is kept, not duplicated, and duplicate_of records it. Mail then call is still one quote.

  6. 6. The write is verified

    A draft counts as drafted only once the system of record returns an id for it. A null id raises an alert.

  7. 7. The draft does not send itself

    The quote is left in a state a person releases. Nothing reaches the customer without that release.

  8. 8. The quote lives in their system

    The quote is created in the system of record the business already runs. Nothing we hold is the copy that counts.

    What a stub cannot prove: The stub proves the build wrote outward and kept no authoritative copy. Only a real tenant proves the write survived that vendor's validation, its required fields, and the shape of its price list.

  9. 9. The log survives

    The source job, the price-list lines used, the draft and the outcome are retained together and retrievable by quote_id.

    What a stub cannot prove: The stub proves the parts are tied together and retrievable. Retention is a property of where it is stored rather than of the build.

  10. 10. The price list was read, not remembered

    Rates are read from the live price list at draft time. A cached copy older than the agreed window parks the quote rather than pricing it.

    What a stub cannot prove: The stub proves the build reads at draft time rather than from a cache of its own. Only a live tenant proves the vendor's API returns the rate the office believes is current.

  11. 11. The pass test passes

    One real job, staged on the real system of record, drafted against the real price list, including a line the book does not cover.

    What a stub cannot prove: Deliberately unpassable on a stub. A stub cannot authenticate into a tenant it does not have, cannot be rejected by that vendor's validation, and cannot prove the catalogue ids it used are the ones the office actually bills on. This is the step the standard says not to hand-roll.

3. The price list is the only source of a rate

Every priced line carries the catalogue id it came from. Not a description that resembles a catalogue line: the id. This is the check that makes the rest of the standard auditable, because it is the one that lets somebody ask, six months later, where a number came from and get an answer.

Three things are therefore banned, and they are banned because each is a plausible-looking thing a language model will do if you let it. A rate inferred from the job description. A rate averaged from similar past jobs. A rate recalled from another customer’s quote. All three produce a number that looks right and is not yours.

The read happens at draft time. A cached price list is how a build quotes last quarter’s rates after the office put through an increase, and the failure is silent: the quote is well-formed, the catalogue ids are real, and the money is wrong. If the cache is older than the window you agreed, or the price list cannot be read at all, the quote parks with pricebook_unreadable.

4. What gets parked

Parking is a real outcome, not a failure. A parked quote carries a reason from a fixed list, and the list is short on purpose:

  • not_in_pricebook: the work is real, the price list does not cover it
  • variation_to_accepted_quote: this changes a quote that has already been accepted
  • discount_requested: the customer has asked for a number off the book
  • pricebook_unreadable: the price list could not be read fresh, so nothing can be priced

The quote is not drafted around the unpriceable line. If one of four lines cannot be priced, the whole quote parks with the other three attached, ready for a person to finish in under a minute. Sending three quarters of a quote is how a business ends up doing the fourth quarter for free.

5. The quote object

One object per job. The fields that carry the weight are external_id, which is null until their system confirms the write, and unmatched, which forces total_ex_gst to null so a parked quote can never present a total.

For your developer. Show the fields
{
  "quote_id": "string",            // ours, stable across retries
  "external_id": "string | null",  // theirs. null means the write failed
  "job_id": "string",              // the job this came from. never absent
  "source_door": "call | email | form",
  "state": "draft | parked",       // never "sent". a person sends
  "lines": [
    {
      "catalogue_id": "string",    // a real id from their price list
      "description": "string",
      "quantity": "number",
      "unit_rate": "number",       // read at draft time, never remembered
      "pricebook_read_at": "ISO-8601"
    }
  ],
  "unmatched": [
    { "description": "string", "park_reason": "not_in_pricebook | variation_to_accepted_quote | discount_requested | pricebook_unreadable" }
  ],
  "total_ex_gst": "number | null", // null whenever unmatched is non-empty
  "duplicate_of": "string | null",
  "drafted_at": "ISO-8601"
}

state has no sent value on purpose. This standard cannot express a sent quote, because it never sends one. That is section 7, and it is enforced by the absence of the field rather than by a rule somebody has to remember.

6. Write-back, and writing once

The quote is created in their system. Not rendered by us and stored with us, not emailed as a PDF we generated: created as a record in the system the office already opens every morning. If the business stops paying us, the quotes are still theirs and still where they expect them.

A write is not done until their system returns an id. A null external_id is an alert, never a completed draft. The failure this prevents is the worst kind: a dashboard saying eleven quotes were drafted last night when four of them exist nowhere.

And one job means one quote, whichever door the job came through. A customer who fills in the web form at 11pm and rings at 7am has created one piece of work and expects one quote. This is the same rule as one job, not two, applied one step further down: if the job layer got dedup right and this layer does not, the customer still gets two quotes.

7. Sending stays with a person

The draft is left in a state a person releases. There is no send port in this standard at all, which is a stronger guarantee than a rule saying not to send: a build cannot accidentally do what it has no capability to do.

This is not timidity about automation. It is where the value actually is. The expensive part of a late quote is the hours between the job existing and somebody starting the draft, and that is the part this removes. The thirty seconds a person spends reading a finished draft and pressing send was never the bottleneck, and it is the thirty seconds that keeps a wrong number off a customer’s screen.

8. Provider and hosting

Build the whole flow against the stub. Every check except section 9 goes green on your own machine, with no tenant, no credentials and nobody to pay.

For your developer. Show the provider config
// The ports this standard observes. Build against the stub, then flip.
interface QuoteOutPorts {
  read_job(job_id: string): Promise<Job>;
  read_pricebook(query: string): Promise<CatalogueLine[]>;
  draft_quote(q: QuoteDraft): Promise<{ external_id: string | null }>;
  escalate(reason: ParkReason, payload: unknown): Promise<void>;
  notify(event: "write_failed", payload: unknown): Promise<void>;
}

// Stub for local development. No tenant, no credentials, no us.
const stub: QuoteOutPorts = makeQuoteOutStub({ seed: "burst-pipe" });

// Production is the same interface against a real tenant. That swap is the
// only thing on this page you cannot do on your own laptop. Ours is the file
// conformance init writes: read_job, read_pricebook and draft_quote on the
// Office Voice MCP.
const live: QuoteOutPorts = await import("./ports.office-voice.mjs");

The swap from stub to a real adapter is the only step that needs anything from anybody else: an authenticated tenant, its price list, and a write that vendor will accept. That is the step this standard says not to hand-roll. Ours is office_voice.draft_quote on the Office Voice MCP, which writes the draft into Xero. Getting the key for it is one command, npx @aipathway/conformance connect: it prints a link and a code, the account owner approves in a browser, and the key lands beside your scaffold. That key does not switch quoting on. Drafting quotes into Xero stays off until the owner reconnects Xero with quote drafting on, and get_setup gives the link.

9. The pass test Take one real job, from a real ServiceM8 or Simpro tenant, or held by your own build when the books are in Xero alone (Xero has no jobs of its own), and quote it against the real price list in that tenant. The work needs three things from the price list and one thing that is not in it. Run 1: no draft is written, and one parked result carries the three priced lines with their real catalogue ids and the fourth line as not_in_pricebook, with no total and nothing sent. Run 2, after a person drops the off-book line: one quote, in draft, in that tenant, with three priced lines. Run 3 repeats run 2 on the same job. There must still be exactly one quote, kept rather than duplicated, carrying duplicate_of.

A stub cannot pass this. It cannot authenticate into a tenant it does not have, it cannot be rejected by that vendor’s validation, and it cannot prove the catalogue ids it used are the ones the office actually bills on. If your build passes the second run and fails the third, your write is not idempotent, which is section 6. If it prices the fourth line in the first run, it is not conforming, and that is the one failure on this page that costs real money.

10. Conformance checklist

A build conforms to this standard when every line is true. Hand this to whoever built yours, including an AI coding agent, and make them answer it.

  • 1. A job is the only trigger. The quote is drafted from an existing job record, never from a raw call or email. Both doors reach this standard through the same job.
  • 2. The price list is the only source of a rate. Every priced line traces to a line in the customer's own price list. No rate is inferred, averaged, or recalled from another job.
  • 3. What cannot be priced is parked. Work with no matching price-list line parks the quote with a reason. It is never estimated, and the quote is not drafted around it.
  • 4. Variations and discounts stay with a person. A variation to an accepted quote, and any discount, escalates rather than drafting. The standard prices the catalogue, not the negotiation.
  • 5. One quote per job. A job already carrying a draft or a sent quote is kept, not duplicated, and duplicate_of records it. Mail then call is still one quote.
  • 6. The write is verified. A draft counts as drafted only once the system of record returns an id for it. A null id raises an alert.
  • 7. The draft does not send itself. The quote is left in a state a person releases. Nothing reaches the customer without that release.
  • 8. The quote lives in their system. The quote is created in the system of record the business already runs. Nothing we hold is the copy that counts.
  • 9. The log survives. The source job, the price-list lines used, the draft and the outcome are retained together and retrievable by quote_id.
  • 10. The price list was read, not remembered. Rates are read from the live price list at draft time. A cached copy older than the agreed window parks the quote rather than pricing it.
  • 11. The pass test passes. One real job, staged on the real system of record, drafted against the real price list, including a line the book does not cover.

Ten of these eleven checks run on your own machine, against the stub, before you talk to anybody. The last one is a live tenant, and that one runs with us. A red result on that last one is not a bug in your build. It is the step this standard told you not to hand-roll.

  1. 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. 2

    Do not hand-roll

    Office Voice is the step not to hand-roll: draft_quote writes the draft into Xero, priced from your own Items, for a person to send. It rides on the Front Desk account sold as Booked After Hours, once Xero is connected with quote drafting on.

  3. 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

This standard sits between two others. Before it, Booked After Hours creates the job it consumes. After it, Follow-Up chases the quote once a person has sent it, and Debtor Chasing collects on the invoice that follows.

For quote follow-up in plain language rather than as a specification, see the lead nurture paper, and for which job system can actually be written to, which job system, honestly.

For whether to build this, buy it, or have someone finish it, see build it, buy it, or have it built.

Published by AI Pathway · https://aipathway.com.au

Read the full interactive version at https://aipathway.com.au/explore-ai/quote-out-build-standard

Want the whole thing by email?

This paper, plus the rest of the library. One click to stop them.

Run these checks yourself

Every check on this page is published as structured data. This prints them with what to inject, what to assert, and which ones can be proved on your own machine rather than against the live thing.

npx @aipathway/conformance show quote-out-build-standard

Free and open source, CC BY 4.0. The checks are fetched from this page rather than bundled, so a correction here reaches you without a release, and you cannot end up conformant against a stale copy. The raw JSON is there if you would rather wire it into your own harness, and the package shows how to drive your build through the ports.

Built it, and one check failed?

Tell us which one. You have already read the standard and tried it, so the answer is a fixed price to finish that step, not a call to work out what you need.