AI ProgramsRetreat
AboutCareersBook a Conversation
Workflows/RevenueBuilt

Email Broadcasts

A marketing email is a list, a body, and an approval. Everything after the approval is a routine: batches go out every 15 minutes, each person is re-checked against the CRM the moment before their send, and the campaign closes itself when nothing is left. Seventy-two hours after that, a second routine posts the settled numbers to the Marketing chat with one sentence from Claude on what they mean, exactly once.

Send tick Every 15 minutesBatch 150 per tick by defaultSummary 72h after send, daily 10:20 (+07)Channel Marketing Lark chat

Three human clicks, then the machine

Nobody presses send on a person. The human decisions are the content, the audience and the go; the routine owns every individual delivery and the report that follows.

01Minutes
Draft and Build the List
Admin
02One click
Test and Approve
Approver
03Now or scheduled
Start Sending
Admin
04Every 15 minutes
Batches Go Out
System
05When nothing is left
Campaign Closes
System
0672h later, daily 10:20
Settled Summary
Claude
Step 04 repeats every tick until the list is empty; step 06 runs once per broadcast, ever.

How each step works

01
Draft the broadcast and build the listAdmin

A broadcast is a row: name, subject, preheader, a Markdown body, the featured posts and call to action as blocks, a sender and reply-to, a batch size (150 by default), an optional schedule and an optional segment. “Build the list” resolves the audience from the CRM through one function that every sending path shares: only people who are subscribed, not marked do not contact, not archived and not in a blocked persona (job seekers never get marketing mail). A guest brand is scoped to its own contacts and can never draw from the house list. The list is paged explicitly so a large CRM cannot be silently truncated.

02
Send a test, then approveApprover

A test goes to the approver’s own inbox (or any address that matches a CRM contact), personalised from that record so the greeting and unsubscribe link are real. Approval is refused if the body is empty or the recipient list has not been built. If the segment carries a send window (a weekday and hour in the contact’s own time zone), approval stamps every recipient with their next such moment, counted from the approval, so “Tuesday 08:00” means the Tuesday after someone said go.

03
Start sendingAdmin

One more click moves the broadcast from approved to sending. Nothing goes out from the button itself; it only makes the campaign eligible for the routine. A scheduled time, if set, holds the campaign until it arrives without blocking any other campaign behind it. A broadcast can be cancelled at any point before it is marked sent.

04
The sender claims a batch and mails itSystemEvery 15 minutes

Each tick picks the oldest campaign that is sending and due, then claims up to one batch of pending recipients in a single database statement (rows whose send window has not arrived are left alone). Claiming and selecting together means an overlapping tick finds nothing to take and cannot double-send; rows claimed by an invocation that died more than 30 minutes ago are returned to pending first.

Then, one recipient at a time: re-check the person against the live CRM (consent, do not contact, archived, persona) and against the bounce and complaint history; skip with a reason if any gate fails; otherwise make one Resend call with the shared blocks, the person’s first name, a single utm_campaign for the whole send and RFC 8058 one-click unsubscribe headers. Sent rows get the Resend id and a timestamp; failed rows keep the error and are not retried. Finally, any webhook events that arrived before the row was stamped are linked back to the campaign.

05
The campaign closes itselfSystemThe tick after the last batch

When a tick finds nothing to claim it counts the rows still pending or claimed. Only when that count is zero does it mark the campaign sent with a timestamp; a batch held by a slower invocation is never abandoned. The pacing is deliberate: the sending domain builds reputation gradually, and a bad list shows up as bounces on the first batch instead of after the whole run.

06
The settled summaryClaudeDaily 10:20 (+07), once per broadcast

Every morning the summary routine looks for broadcasts marked sent at least 72 hours ago that have no summary yet (at most 20 per run). For each it reads sent, delivered, opened and clicked from the event ledger, clicks by topic from the utm_content tag on each link, and unsubscribes attributed by time since approval. Claude gets those numbers and returns one sentence on the single most useful thing they say; if it is unavailable the post goes out without the line.

The post lands in the Marketing Lark channel with a link to the results page, and only then is the broadcast latched as summarised, so a failed run is retried the next day and a successful one is never repeated.

Every exception has an owner

Sending mail is the one automation where a mistake reaches a stranger’s inbox, so most of the code is the off-path. Each row is a real condition, what the routine does, and where a human hears about it.

ConditionWhat happensWhere you hear about it
Person unsubscribed, archived, marked do not contact, or lost consent after the list was builtRow marked skipped with the reason; no mail sentThe recipient table on the broadcast page
Address previously hard-bounced or reported spamRow marked skipped; a transient bounce (full mailbox) does not countThe recipient table
CRM lookup fails mid-batchRow returned to pending and retried next tick; a timeout is never treated as a suppressionRun log, as deferred
Resend rejects a sendRow marked failed with the error; never retried, so a bad address cannot burn reputationThe recipient table
Mail accepted but the row update failsCounted as a write failure; the mail is not resentRun log, as writeFailures
A tick dies mid-batchRows claimed more than 30 minutes ago go back to pending; the next tick takes themNowhere, by design
Two ticks overlapRows are claimed and selected in one statement; the second tick finds nothing to takeNowhere, by design
Broadcast scheduled for laterIgnored by the picker until its time arrives, so it never blocks another campaignThe broadcast page shows the schedule
A send window is set (weekday and hour in the contact’s zone)Each recipient carries its own earliest moment; the claim skips rows whose moment has not comeThe broadcast page
Claude unavailable for the takeawaySummary posts with the numbers and no takeaway lineThe Lark post, shorter
Summary posted but the latch write failsLogged; the broadcast is picked up again tomorrow, so a second post is possibleRun log; the duplicate in Lark
Marketing webhook unset or Lark downLogged and swallowed; the latch still sets, so the recap is not re-attemptedRun log only

The seven elements

Every workflow we document has the same anatomy: seven elements, each assigned to a human, a machine, or both. This is the Centaur Map from our workflow design method.

01 TriggerBoth

A human approves and starts the broadcast. From then on it is time: a tick every 15 minutes for the batches, a daily run at 10:20 (+07) for the summary.

02 InputsMachine

The broadcast row (subject, body, featured posts, batch size, optional schedule), its recipient list, the live CRM record of each person, and the bounce and complaint history from Resend.

03 DecisionBoth

Humans decide the content, the segment, and when to go. The sender decides, per recipient and at the moment of send, whether that person may still be mailed. It never decides who to add.

04 RoutingMachine

One campaign at a time, oldest due first. One batch per tick. The summary goes to the Marketing Lark channel; the results page holds the detail.

05 OutputMachine

One email per eligible recipient, a status on every recipient row (sent, skipped with a reason, failed with the error), and one Lark recap per broadcast.

06 DeliveryMachine

Resend carries the mail with one-click unsubscribe headers. Delivery, open, click and bounce events flow back by webhook and are linked to the broadcast.

07 MeasurementBoth

The machine waits 72 hours, then reports sent, delivered, open, click, unsubscribes and the top topic. A human reads the takeaway and decides what the next letter does differently.

The standing rules

  • One function decides who may receive marketing mail; the sender and the audience count both call it
  • Every recipient is re-checked at send time, never trusted from the list
  • A database error is a retry, never a suppression
  • A send failure is recorded, never retried
  • One campaign at a time, one batch per tick, oldest due first
  • A summary is posted once; the latch is set only after the post

How we know it works

  • Every run writes a row to the routine log with sent, skipped, failed, deferred and write-failure counts
  • The results page reconciles recipient statuses against the event ledger
  • A broadcast still pending the morning after its last batch is the alarm
  • A summary that never arrives means the latch or the webhook, both visible in the run log