Dispatch logodispatch
‹ Sales & Revenue Ops
Outreach drafting · chained

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.

Steps12 Generative2 of 12 Cost / run$0 StartsOn the report finishing
Pipeline 1 · 08:00

Dormant accounts report

Finds every rep's stalled opportunities, opens each customer's site in a real browser, writes the CSV.

Pipeline 2 · on completion

Outreach drafting

Reads that same CSV, researches each account, briefs it, groups by contact, writes the emails.

The rep

Read, edit, send

Or take the raw list and write them yourself. Or let it send. Same pipeline either way.

01

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.

The subscription is scoped to one workflow by id, so a chain waits on its upstream rather than waking on every pipeline that happens to finish. And a workflow is never restarted by its own completion — an obvious-in-hindsight guard, without which a pipeline subscribed to this event would run forever.
02

Twelve steps, two of which involve a model

01

Read the report the first pipeline just wrote

assets.read · dormant-accounts-{{date}}.csv

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.

deterministic
02

Take this rep's five highest-value accounts

utils.csv_to_json → utils.jq_query

Filter to one rep, sort by value, cap at five. A parsed table and a sort, not a model deciding who matters today.

deterministic
03

Pull the full history for each

foreach · six CRM lookups per account

Outreach notes, plan and payment status, contact record, billing history, lifetime revenue, migration complexity. Six calls per account, two accounts at a time.

∥ foreachdeterministic
04

Brief each account on its own

foreach · agent · transform

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.

∥ foreachgenerative
05

Group the briefs by contact

utils.jq_query · group_by(email)

Agencies manage many sites under one address. Five accounts routinely collapse to two people — and two emails, not five.

deterministic
06

Write one email per person

foreach · agent · transform

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.

∥ foreachgenerative
07

Deliver, and keep a copy

slack.send_message · assets.write

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.

write · auto
03

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.

The one who won't

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.

stops after pipeline 1
Most people

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.

stops before send
The one who would

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.

adds a send step
This is what a DAG buys that an agent doesn't. Every step is a named, inspectable boundary, so "where does the human belong?" is a question you answer per rep, in configuration — and change your mind about next week. With an autonomous agent the honest answers are "at the end" or "watching the whole thing", and neither is a dial.
04

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.

01Run it

On real data, on demand — from the same chat window you'd use to ask about the data.

02Read what it did

Every step's input and output is kept. Bad field, bad step — you can see which.

03Change one step

Described in a sentence over MCP. A row in your box's database, live on save.

Run it again

About two minutes end to end. Local models, so the twentieth attempt costs the same as the first: nothing.

wrong source

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.

wrong shape

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.

wrong reasoning

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.

wrong data

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.

wrong audience

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.

wrong format

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.

None of these were visible in the design. All six were visible in the first real output — which is why how well you spec it matters less than how fast you can look at what it actually produced and change exactly one thing. Each fix above was a sentence typed into a chat window and a re-run, and every one of them is configuration: a binding, a step boundary, a line in a skill file. With an autonomous agent these are one prompt rewritten six times, with no way to tell which instruction caused which symptom. Here each had an address.
05

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 foreach step 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
Nothing on the left is specific to outreach. A completion trigger chains any two pipelines; 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 takeaway

The handoff is a tool, so you choose where it stops.

Shipping today · starts when the morning report finishes