Case study
Dispatch Desk
Approval-gated email automation for client-facing teams
Working prototype — built to demonstrate the approach to prospective clients
An agentic email workbench for teams whose inboxes carry client correspondence. It reads, triages, drafts and schedules against a live mailbox — and stops for a human decision before anything is sent, because unsupervised automation is not something a business will put in front of its customers.
The problem
In any business that runs on client correspondence, a large share of skilled time goes on the inbox rather than the work. Someone reads every message to find the few that matter, then writes similar replies to similar requests. The expensive part is the reading, not the typing.
The same shape appears wherever documents queue up to be judged — an HR team opening every CV to find a shortlist has exactly the same bottleneck.
No business will let software send mail to its customers unsupervised. Most demonstrations of this idea quietly assume the agent may send — and that assumption is what makes them undeployable.
A wrong internal summary is an annoyance. A wrong message sent to a client, in the company’s name, cannot be recalled. So the requirement is not an AI that handles email. It is one that does the reading at machine speed and then stops, without making the stopping so frequent that the time saved is given back.
What it does
It loads the inbox in about a second, classifies unread mail by priority and whether it genuinely needs a reply, drafts responses in the correct conversation thread, and proposes meeting times from real calendar availability.
Reversible actions — opening, archiving, marking read — happen on a click. Irreversible ones stop at an approval showing exactly what would happen, where it can be accepted, edited or refused with a reason.
- To
- jane@northwind.exampleexternal1 of 5 allowed
- Subject
- Re: Coffee next week?
- Thread
- ↳ Replying in thread 1a0527a2dd
Hi Jane,
Tuesday works — I have 3pm free. Shall we say the place on Wells Street?
Best, Bijith
How it works
The design question that mattered was not how to give a model email tools. It was deciding what should not go through the model.
A first version routed everything through the agent. Fetching the inbox took over thirty seconds, because a model had to read the request and decide to call a search tool. The same fetch made directly took about a second and a half. Those two numbers settled the architecture.
Why it can be trusted
Approval exists to gate actions a model chose. Clicking Archive on rows you selected is already your decision, so the direct path does not prompt. Asking the assistant to archive does — and that asymmetry is deliberate.
The exemption is bounded by an allowlist with no send and no delete in it, by construction rather than convention. Tools are classified by verb, and anything unrecognised is treated as a write.
The other half of trust is being able to answer why did it do that? Every step of a run is a durable checkpoint recording the state and the exact node about to execute — forty of them for a single conversation — so a decision can be reconstructed after the fact rather than guessed at.
What went wrong
All found by using the application rather than reading the code — the argument for driving a thing end to end before believing it works.
- A receipt reported “Delivered” on a rejected send.The rejected call returned a result carrying an error status, and the interface read the presence of a result as evidence of delivery.
- A calendar tool classified as a read and would have auto-approved.create_calendar creates a calendar, but the rule required the noun “event”. Classification became verb-first, with a regression test over the whole class.
- A multi-select archive silently affected only the first message.The label tool accepts one id; the extra argument was dropped as unknown while the interface removed every selected row.
- The inbox rendered empty against a working API.The listing parser did not allow for the number preceding the first field of each record, so every row was skipped.
- A prompt told the model to confirm before sending.That duplicated the approval step, so it asked in conversation and never called the tool. Nothing sent, nothing visibly wrong.
Threading was verified against Gmail directly rather than trusted from the agent’s report: after sending, the conversation was queried and both messages confirmed to share one thread identifier.
Where it goes next
The hiring bottleneck in the problem statement is the same architecture pointed at a different queue. Applications arrive in the same mailbox, the attachment tool already reaches the documents, and the pattern that classifies unread mail is the one that would evaluate a CV.
It screens and shortlists; it does not reject. Under the EU AI Act and comparable regimes, software that automatically rejects candidates is a high-risk use with obligations attached — so the human stays in by design, not by retrofit.
Status and provenance
A working prototype, not a delivered engagement. It runs end to end against a real Gmail account — my own — and the behaviour above was verified against live mail: replies confirmed to land in the right conversation, rejections confirmed to send nothing, archiving confirmed against Gmail rather than trusted from the interface. The screenshots use invented correspondents.
It began as a teaching example whose architecture was sound and whose implementation was entirely stubbed — the inbox returned a hardcoded string, sending returned a formatted string, authentication compared a password to a constant. Keeping that architecture while replacing every input and output with a real mailbox is the work described here.
Adapting it to a business would mean its mail conventions, its rules on who may be written to, its retention and residency requirements, and deployment in its own cloud account. The architecture anticipates that: the credential is server-side, the tool surface is scoped at launch, and the approval rules are derived rather than hand-maintained.