← All standards
Open specificationStablePublished November 2026Changelog

The Exception Desk Build Standard

One list for everything your AI, your sheets and your staff could not decide, with an owner and a clock on every item and a person to close it. Whichever way each step runs. Written so you can build it yourself.

Used by

  • Office Manager
  • Operations Manager
  • Office Administrator
  • AI Officer

9 min read · Published by AI Pathway

Using Claude? This standard installs as a skill. Guides for ServiceM8, Xero and Simpro.

What this is

An open build standard for the things your AI, your sheets and your staff could not decide. An exception is one of those things, like "line 3 of the Patel quote isn't in the price book". Every one goes to one list, wherever it came from, so the office checks one place instead of five.

The core rule

Anything can put an item on the list. Only a person takes one off, and they write what they did. Who owns each kind of item, and how long they have, comes from a table you write, never from the AI that raised it.

What it does not do

It never closes an item because the problem seemed to go away, never lets the AI choose who should look at it, and never does anything about a dispute or a hardship claim except hand it to the person you named and stop every chase on that invoice.

The hard part

A sheet, a phone call and a bot, built by different people, all writing to the same list with the same job ids. The rules are simple; getting every surface to follow them is the work.

On Monday the quote for the Patel hot water job stopped on a heat pump that was not in the price book, and the AI emailed the owner. The parts check found the tempering valve short and wrote it on a sheet tab. The after-hours call could not place an address and handed it to the Office Voice inbox. A Teams message said the job card write to ServiceM8 had failed. By Thursday all four were still waiting, in four places, and nobody had been told any of them was late.

Everything parked, in one place

  1. Every standard, every surface

    Office Voice, a bot, a sheet, Microsoft 365, your own code. Each parks what it cannot decide.

    • record id
    • type
    • reason

    raises into

  2. The queue

    One item per record and type. A repeat updates it; it never adds a row.

    • owner
    • due_at
    • stop_chase

    routed by

  3. Your routing table

    Who owns each type, the clock, who it goes to when late. You write it.

    • type
    • owner
    • clock_hours

    closed by

  4. A person

    Closes each item with what was done. The morning summary shows the rest.

    • closed_by
    • outcome

    reported on

Read it downward. Every standard, however it runs, parks into the same queue; your routing table decides who owns each item and how long they have; only a person takes an item off. The arrows are what this standard checks.

One exception, through the desk

  1. Any standard, run any way, parks something it cannot decide.
  2. The desk reads the record by its id from the system that holds it. No id, or an id the system does not hold, goes back to the standard that raised it.
  3. The owner and the due time come from the routing table the business wrote, never from the agent that raised it.
  4. If an item is already open for that record and type, it is updated; otherwise one is written.
  5. Has a person closed it, with an outcome? If not, it stays open and is on every morning summary.
  6. When a person closes it, the item records who, when and what was done.
  7. Was it closed before it was due? If so, it leaves the queue and is counted on the summary.
  8. If not, it is escalated once, to the person routing names, and still waits for a person to close it.
The filled diamond is the person: nothing takes an item off the queue but someone closing it with an outcome. The loop is the clock: an item past its due time escalates once, then still 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 running more than one automated step, where each step sometimes has to stop and ask a person. The examples here are a trade business, because that is where the most steps are written down as standards, but nothing in the checks is about trades. It is not a step on the job’s path. It is the place every other standard writes to when it parks something, and it is the last row on the field service automation roadmap.

Two older patterns carry it. The first is the in-tray: anyone can drop a note in, and only the person whose tray it is takes one out. The second is the rota on the office wall: who looks after what is written down once, by the business, not decided by whoever happens to notice the problem.

Out of scope. Working the exception. The desk holds it, routes it, times it and reports it; the person fixes the price book, rings the supplier or calls the customer back. A complaint taken on a call is still Client Desk’s to handle; it lands here as a dispute so every chase stops.

What cannot meet it. Anything that cannot name the record by an id. ServiceM8, Simpro, AroFlo and Fergus each have an API a build can read a job back by its id, and Xero does for an invoice or a quote. Tradify has no public API, so a Tradify job cannot be read by its id: a business on Tradify parks against the Xero invoice or quote id instead, and a job with neither is checked by a person before it is raised.

2. The records it keeps, in the shape your developer needs

One item, open or closed, and the routing table the business writes. The fields a person reads on the queue come first; the close comes last.

For your developer. Show the fields for your developer
Exception                           # one per open record and type
  - id
  - raised_by { standard,           # quote-out, a bot's name
                surface }           # office_voice | bot | workspace | m365 | code
  - type                            # from the closed list below
  - record { system,                # servicem8 | simpro | aroflo | fergus | xero
             external_id }          # the id that system issued. Never a name.
  - reason                          # a label off the list is kept here
  - evidence_uri?                   # transcript, photo, remittance
  - owner                           # from Routing, by type
  - due_at                          # raised_at + Routing clock_hours
  - raise_count, last_raised_at     # a repeat updates, never adds
  - resolved_upstream               # the cause went away. Still open.
  - stop_chase                      # dispute | hardship: every chase holds
  - escalated_at?, escalated_to?    # once
  - status    open | closed
  - closed? { by, at, outcome }     # a person. Never a machine.

type: price_not_in_book | variation | unassignable | parts_shortfall | write_failed | address_unresolved | promise_broken | dispute | hardship | stock_unknown | needs_list_unknown | unclassified_fault | tech_unconfirmed | other

Routing                             # the business writes this
  - type, owner, clock_hours, escalate_to?
  - summary_at                      # the morning summary's time

Summary                             # one per day, even when empty
  - day, by_type{}, by_owner{}, overdue[], other_labels[], none

An item with no record id, an owner the raising agent chose, and a close with no person behind it are the three defects this schema exists to make visible.

3. The checks, in order

The order follows one item. It names its record, gets its type, owner and clock, is kept to one row, waits for a person, is held for a person if it is a dispute, escalates if it runs late, and is counted every morning.

  1. Every item names its record by id, read from the system that holds it. An item points at the job, quote or invoice it is about by the id the system of record issued, and the desk reads that record before it writes the item. A raise with no record id, or with an id the system does not hold, is sent back to the standard that raised it, and nothing is written.
  2. The type comes from the closed list. Every item carries one type from the standard's closed list. A raise with any other label is stored as other, with the label it arrived with kept in the reason, and never as a new type. The daily summary lists the labels that arrived as other, so one that keeps recurring becomes a type by a person's decision.
  3. The owner and the clock come from the routing table, never from the agent that raised it. The business writes a routing table: for each type, the person who owns it, how many hours they have, and who it goes to if it is late. The desk reads the table before it writes an item and takes the owner and due time from it. An owner or due time the raising agent suggests is ignored.
  4. One item per record and type: a second raise updates the open item. The desk reads the open items before it writes. A second raise of the same type on the same record updates the open item's reason and raise count and adds no row, and it does not restart the clock. A different type on the same record is a separate item. Once a person has closed an item, a new raise opens a new one.
  5. Nothing closes itself: a person closes every item, with an outcome. The desk has no way to close an item. A raising standard may mark an item resolved upstream (the invoice was paid, the part turned up), and the item stays open with that mark until a person closes it with an outcome. A close is recorded with who closed it, when, and the outcome they wrote.
  6. A dispute or a hardship claim is never worked by an agent. A dispute or hardship item goes to the person the routing table names for it, and carries a stop that every chase standard reads before it acts on that record (Debtor Chasing, Follow-Up, Cash Seam). The desk sends nothing about it to anyone but that person, whatever the raising agent suggested doing next.
  7. An overdue item escalates once. When an open item passes its due time, the desk escalates it once, to the person the routing table names for that type. It does not escalate early, does not escalate again on the next run, and never escalates a closed item.
  8. The daily summary goes out by the set time, including when there is nothing in it. Once a day, by the time the business set, the desk publishes a summary: open items counted by type and by owner, the overdue items listed, and the labels that arrived as other. When nothing is open it still publishes, and says none.
  9. The pass test passes. On the business's own systems, three exceptions from three different ways of running land in one queue: Invoice Out's sheet parks an invoice on an unpriced line, a Front Desk call escalates an address it could not resolve, and a bot raises a parts shortfall. Each has its record id, its owner from the routing table and a due time. A second raise of the shortfall leaves one item, one item going overdue escalates once, and the next day's summary lists all three.

4. Who parks what into it

Every standard already says what it must not decide. Conforming to this one changes where that goes: instead of an email, a sheet tab or a chat message, the standard raises an item here, against the id it already carries from Cash Seam.

StandardWhat it parksArrives as
Booked After HoursAn address the call could not resolve, or a caller disputing a bill.address_unresolved, dispute
Quote OutA line the price book does not cover, or a variation nobody has priced.price_not_in_book, variation
Dispatch BoardA job nobody free is licensed or close enough for.unassignable
Material OrderA part no supplier can get there in time, or stock nobody can confirm.parts_shortfall, stock_unknown
Invoice OutAn invoice held on an unpriced line, or a write the ledger refused.price_not_in_book, write_failed
Debtor ChasingA promise to pay that was broken, or a customer asking for time.promise_broken, hardship

The chase standards also read from it. Before Debtor Chasing, Follow-Up or the Cash Seam hand-over acts on an invoice, it checks for an open dispute or hardship item on that record, and stops if there is one.

5. Run it your way

The point of the desk is that it does not care how each step runs. A business can answer the phone on Office Voice, quote from a sheet, check parts with a bot and close jobs in its own code, and still have one list. The same checks apply to every way; here is where each one stands today.

  • Done for you, on Office Voice: partly. When an Office Voice call hands something to a person, it lands in Office Voice’s inbox with a typed reason, today. Letting a sheet, a bot or a flow write to that same inbox, and read it back, is being built and is not live yet.
  • Your own Grok Bot or Claude: by appending a row. A bot raises an item by adding a row to your sheet or list, and never closes one. Section 8 has the instructions. Raising straight into Office Voice from a bot comes with the same build.
  • Google Workspace: the default. One Exceptions sheet every other template appends to, a Routing tab, an hourly run that escalates once, and a morning summary by email. Our copyable sheet is built and goes on this page once its link is shared; until then, the Workspace guide covers building it.
  • Microsoft 365: a build plan. One SharePoint list every flow appends to, a routing list, closes only a person can make, and a Teams summary each morning. The Parked Items plan lays out the lists and flows.
  • Your own code: this page. The checks, the item, a runnable stub and the conformance checklist. An AI coding agent can build against it and run the checks on your own machine.

Mixing them is safe as long as each one writes to the same queue with the record id it read, and names its surface. See run a standard anywhere.

6. One morning on the desk

Harding Plumbing’s routing table gives parts shortfalls to Mel, the office manager, with four hours on the clock and Dana, the owner, as the escalation. Disputes go straight to Dana. The summary is set for 7am.

  1. Monday, 9:10am. Raised. The parts bot finds the tempering valve for job J-1042 short and raises a parts shortfall, suggesting it handle the item itself. The desk reads J-1042 from ServiceM8, takes Mel and a 1:10pm due time from the routing table, and ignores the suggestion.
  2. Monday, 11:00am. Raised again. The parts sheet raises the same shortfall: the supplier now says Thursday. The item is updated with the new reason and a raise count of two. Its due time is still 1:10pm.
  3. Monday, 11:40am. A dispute. A customer rings Front Desk about invoice INV-2207 and says the second visit was a callback. The call raises a dispute. It goes to Dana with the call attached, and Debtor Chasing will not touch INV-2207 while it is open.
  4. Monday, 2:00pm. Escalated, once. Nobody has closed the shortfall. The hourly run escalates it to Dana. The 3pm run, and every run after, does not.
  5. Monday, 3:15pm. Closed by a person. Mel rings the wholesaler, gets a valve on the 7:30 run and closes the item with that outcome. Her name and the time are on it.
  6. Tuesday, 7:00am. The summary. One open item, the dispute, owned by Dana, not yet overdue. On a morning with nothing open, the summary still goes out and says none, so the office knows the desk was checked.

7. What stays with a person

  • Closing every item, with what was done.
  • Writing the routing table: who owns each type, how long they have, who it goes to when late.
  • Every dispute and hardship claim, start to finish.
  • Deciding when a label that keeps arriving as other deserves a type of its own.
  • Fixing the cause: the price book, the supplier, the address, the write.

8. Let a Grok Bot or Claude raise items, without closing any

A bot that builds your debtor list or chases your quotes will hit things it should not decide. The rule for a bot is the rule for every surface: raise the item against the record’s id, name yourself and your surface, and never close, re-own or act on what you raised.

  1. Give the bot somewhere to write: the Raise tab of your exceptions sheet, or the SharePoint list from the Microsoft 365 plan, through whatever connector your bot uses for that sheet or list. Its access should add rows and nothing more.
  2. Tell it, in its standing instructions: when you cannot decide something, add one row with your name, surface bot, the type from this list (price_not_in_book, variation, unassignable, parts_shortfall, write_failed, address_unresolved, promise_broken, dispute, hardship, stock_unknown, needs_list_unknown, unclassified_fault, tech_unconfirmed, other), the system and the record id you read it from, and the reason. Do not suggest an owner. Never close or edit a row.
  3. Tell it to read the queue for open dispute or hardship items on an invoice before it chases that invoice, and to skip any it finds.
  4. Run the checklist in section 11 against a real record.

Coming on Office Voice. A raise_exception tool, and a list_exceptions read, on the same Office Voice key a bot already uses for Xero, are being built. They are not live. When they are, a bot will raise into the Office Voice inbox with the same closed list, and Office Voice will keep one item per record and type and take the owner from your routing table. Closing will stay on Office Voice’s own screen.

Facts that can change, and when we checked them

Checked as at 1 November 2026

Office Voice’s inbox holds what its own calls hand off, each item with a typed reason. It does not yet accept items from a sheet, a bot or a flow, and it offers no tool to raise or list them; that is being built and is not live. Until it is, the one queue for every other surface is the business’s own sheet, list or table, and the provider block below says so.

Tradify has no public or partner API, confirmed in writing by their support on 10 April 2026.

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.

10. What your developer builds, and the step that needs care

No step here passes the twelve-month test on its own: a queue, a routing table and a summary are a day's work. The standard exists because every other standard needs somewhere to park, and because its refusals matter. The nearest hosted piece is Office Voice's inbox, for the items its own calls raise.

For your developer. Show the setup for your developer
provider:
  stub:       local.mock_exception_queue
  production: none                       # scoped build, not a product

today                                # Office Voice
  - call handoffs land in its inbox with a typed reason

coming, not live                     # Office Voice
  - raise_exception and list_exceptions on the MCP key
  - one item per record and type, owner from your routing

stays local                          # yours, and the standard
  - the queue for every other surface
  - the routing table and the morning summary
  - every close, by a person

Ask anyone selling an AI that runs your office one question: show me everything it parked this week, who owns each one, and which are late.

The pass test. On the business’s own systems, not a sample. Raise three exceptions from three different ways of running: Invoice Out’s sheet parks an invoice on an unpriced line, a Front Desk call escalates an address it could not resolve, and a bot raises a parts shortfall. All three must appear in one queue, each with its record id, its owner from your routing table and a due time. Raise the shortfall again: still one item. Let one go overdue: one escalation, and none on the next run. The next morning’s summary lists all three. A stub cannot pass this: on a stub every raise comes from the runner in one shape. Only three surfaces built by different people, writing to the same queue with the same record ids, prove it.

11. A checklist to hold any build or supplier to

Hold a DIY build or a vendor to this. If a box is empty, it is not in production.

  • 1. Every item names its record by id, read from the system that holds it. An item points at the job, quote or invoice it is about by the id the system of record issued, and the desk reads that record before it writes the item. A raise with no record id, or with an id the system does not hold, is sent back to the standard that raised it, and nothing is written.
  • 2. The type comes from the closed list. Every item carries one type from the standard's closed list. A raise with any other label is stored as other, with the label it arrived with kept in the reason, and never as a new type. The daily summary lists the labels that arrived as other, so one that keeps recurring becomes a type by a person's decision.
  • 3. The owner and the clock come from the routing table, never from the agent that raised it. The business writes a routing table: for each type, the person who owns it, how many hours they have, and who it goes to if it is late. The desk reads the table before it writes an item and takes the owner and due time from it. An owner or due time the raising agent suggests is ignored.
  • 4. One item per record and type: a second raise updates the open item. The desk reads the open items before it writes. A second raise of the same type on the same record updates the open item's reason and raise count and adds no row, and it does not restart the clock. A different type on the same record is a separate item. Once a person has closed an item, a new raise opens a new one.
  • 5. Nothing closes itself: a person closes every item, with an outcome. The desk has no way to close an item. A raising standard may mark an item resolved upstream (the invoice was paid, the part turned up), and the item stays open with that mark until a person closes it with an outcome. A close is recorded with who closed it, when, and the outcome they wrote.
  • 6. A dispute or a hardship claim is never worked by an agent. A dispute or hardship item goes to the person the routing table names for it, and carries a stop that every chase standard reads before it acts on that record (Debtor Chasing, Follow-Up, Cash Seam). The desk sends nothing about it to anyone but that person, whatever the raising agent suggested doing next.
  • 7. An overdue item escalates once. When an open item passes its due time, the desk escalates it once, to the person the routing table names for that type. It does not escalate early, does not escalate again on the next run, and never escalates a closed item.
  • 8. The daily summary goes out by the set time, including when there is nothing in it. Once a day, by the time the business set, the desk publishes a summary: open items counted by type and by owner, the overdue items listed, and the labels that arrived as other. When nothing is open it still publishes, and says none.
  • 9. The pass test passes. On the business's own systems, three exceptions from three different ways of running land in one queue: Invoice Out's sheet parks an invoice on an unpriced line, a Front Desk call escalates an address it could not resolve, and a bot raises a parts shortfall. Each has its record id, its owner from the routing table and a due time. A second raise of the shortfall leaves one item, one item going overdue escalates once, and the next day's summary lists all three.
  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

    There is no product for this step. Highest option: none. Checklist plus a build.

  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

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 exception-desk-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.