Automate the whole morning.
Or any part of it.
When the dormant-accounts report finishes, it starts a second pipeline: research every account on the list, write one follow-up per contact, and hand it over. Where a rep takes back the wheel — after the list, after the draft, or not at all — is a setting on their own copy, not a different product.
Dormant accounts report
Finds every rep's stalled opportunities, opens each customer's site in a real browser, writes the CSV.
Outreach drafting
Reads that same CSV, researches each account, briefs it, groups by contact, writes the emails.
Read, edit, send
Or take the raw list and write them yourself. Or let it send. Same pipeline either way.
The second pipeline starts because the first one finished
trigger · workflow.succeeded, scoped to one upstream workflow by id
The obvious way, and why it's wrong
Two cron jobs. Report at 08:00, drafting at 08:15.
Fifteen minutes is a guess. On a slow morning the report is still running and the drafting job reads yesterday's file, or nothing, and writes confident emails about stale data. Nobody finds out, because a pipeline that ran on the wrong input still succeeds.
What actually happens
The drafting pipeline subscribes to the report's completion.
No timer, no guess. The report emits an event when it finishes; the drafting run starts from it. If the report takes an hour, drafting waits an hour. If the report fails, drafting never runs at all — which is the correct behaviour and the one a schedule can't express.
Twelve steps, two of which involve a model
Read the report the first pipeline just wrote
Not a fresh query. Reading the upstream pipeline's own output means the drafts and the report describe the same snapshot, down to the site checks — they cannot disagree.
Take this rep's five highest-value accounts
Filter to one rep, sort by value, cap at five. A parsed table and a sort, not a model deciding who matters today.
Pull the full history for each
Outreach notes, plan and payment status, contact record, billing history, lifetime revenue, migration complexity. Six calls per account, two accounts at a time.
Brief each account on its own
One model call per account, seeing only that account. It decides the angle from a fixed date rule — lapsed, renewing, or renewing after the platform is switched off — and picks the single fact worth opening on. Small context, one decision, which is where a local model is reliable.
Group the briefs by contact
Agencies manage many sites under one address. Five accounts routinely collapse to two people — and two emails, not five.
Write one email per person
The second and last model call. It sees finished briefs, never raw data, so it cannot invent a figure. When someone's accounts disagree — one renewing, three lapsed — it has to say so rather than flatten them into one convenient claim.
Deliver, and keep a copy
The drafts land where the rep works, formatted and ready to use, and the day's set is archived as JSON so the delivery method can change without touching the pipeline.
Reps don't agree about automation. They don't have to.
The same pipeline, stopped at three different points. Not three products, not a rollout, not a company-wide policy — a copy of the workflow per rep, each ending where that rep wants it to.
Just give me the list
Stops after the first pipeline. A ranked CSV of stalled accounts every morning, with the site already checked, and they write every word themselves. The research they'd have done by hand is done; the relationship is entirely theirs.
Draft it, I'll decide
The full chain, ending in drafts they read, edit and send. The judgment stays with the rep, and the hour of pulling records and staring at renewal dates does not. This is the one running today.
Just send them
One more step on the end. Send can gate on approval — a tap on a phone, per message — or run unattended once it's earned that. The pipeline doesn't change shape; the last step just stops waiting.
Nobody specs this correctly the first time
The first run produced emails you would never send. So did the second. Getting to output that goes out unedited is a loop, and the only thing that matters is how tight the loop is.
On real data, on demand — from the same chat window you'd use to ask about the data.
Every step's input and output is kept. Bad field, bad step — you can see which.
Described in a sentence over MCP. A row in your box's database, live on save.
About two minutes end to end. Local models, so the twentieth attempt costs the same as the first: nothing.
What came outAccounts that didn't match the report that triggered the run — the pipeline was querying the CRM again, so two snapshots could disagree.
What changedOne binding. Read the file the first pipeline already produced. One snapshot, one set of site checks, no drift.
What came outEvery email in one voice. A single model call was writing them all at once, and distinct accounts flattened into the same three sentences.
What changedSplit into two steps — a brief per account, then compose from the briefs. Each call gets one account's context instead of twelve.
What came outRenewal dates already months in the past, described as upcoming. Asked to reason about dates, it reasoned badly and confidently.
What changedStop asking. An explicit decision table in the skill: given today, which branch applies. The model reads the row, it doesn't do the arithmetic.
What came outA price of $0 quoted back to the customer as though it were the real figure.
What changedA rule: zero is missing data, not a price. Never state it, flag it instead.
What came outInternal scoring in customer-facing copy — a migration-complexity rating, in the body of the email.
What changedTwo fields instead of one. The email is what the customer reads; everything else goes where the rep reads it.
What came outOne unbroken wall of text. Worth checking before blaming the renderer: there were no line breaks in the output at all.
What changedAn instruction requiring paragraph breaks, with a worked example. Not a formatting bug — a missing instruction.
What the platform gives you — and what you wire up
Building this was composing primitives, not writing code. Worth seeing where the line sits when you scope your own.
Dispatch provides
Built-in capability — available to every workflow, on every box.
- completion triggers between workflows
- the
foreachstep kind - CSV, JSON and jq transforms
- skills, approvals, run history
✓ You wire up
A row in your box's database. Live the moment you save it — no deploy, no rebuild.
- which pipeline feeds which
- whose accounts, and how many
- the two agent prompts
- where it stops, per rep
foreach runs any per-item work. That is what makes the right-hand column an afternoon rather than a project — and what makes a per-rep variant a duplicate-and-edit rather than a fork.
The handoff is a tool, so you choose where it stops.
Shipping today · starts when the morning report finishes
dispatch