Workflows
Drip campaigns and lifecycle automation on your own SES
Write workflows as readable text — SendFlow — or design them on a visual canvas. It's the same file, two views. Trigger on events, contact activity, and segment membership. Every email goes out through your own SES account: your infrastructure, your sending reputation, your rates — never shared sending infrastructure.
The problem
Lifecycle email lives in one of two bad places
The first is a journey builder you rent. You drag boxes around a canvas, the logic lives in someone else's database, and the bill is priced per contact — it grows with your list whether you send or not. There's no version history, no diff, no way to review a change before it goes live, and no way to move the campaign anywhere else. When the flow breaks, you find out from a customer, and the fix is more clicking.
The second is code you maintain: cron jobs, queues, a state machine per contact that someone built in a sprint two years ago. It's yours, but the drip logic is smeared across workers and database rows. Changing "wait 2 days" into "wait 3 days unless they've logged in" is a deploy, a migration, and a prayer for the contacts already mid-flow.
Neither one is reviewable, versioned, or operable by a coding agent. Click-ops can't be diffed. Homegrown queues can't be handed to marketing. Lifecycle email deserves the same treatment as the rest of your stack: a readable artifact, a review process, and infrastructure you already own.
SendFlow
A drip campaign you can read top to bottom
SendFlow is a small, purpose-built language for email drips. It's structured — sequence, if/else, bounded repeats, timed waits, named exits. No gotos, which is exactly what guarantees every flow maps losslessly onto a diagram. Conditions are SendQL, the same query language that powers your segments, embedded as real syntax rather than quoted strings.
- One syntax tree powers both the text editor and the visual canvas — edit either, they stay in sync
- Workflows live as .flow files in your repo — PR-reviewed, synced on merge — or edited in-app with typeahead
- Waits, branches, holdout splits, and frequency caps are all part of the language, not settings hidden in a UI
workflow "Trial onboarding" v1 {
enter on segment "trial-started"
exit "converted" when attr.plan != "trial"
send "welcome" via topic onboarding
wait 2 days
if not exists(open where template = "welcome") {
send "welcome-reminder" via topic onboarding
wait 2 days
}
send "activation-tips" via topic onboarding
wait up to 7 days until count(login within 7d) >= 2 {
timeout: send "need-a-hand" via topic onboarding
}
}The canvas
The same workflow, drawn — not a second source of truth
The canvas isn't a separate builder that exports to code. It's the same file, rendered. Anything you can draw, the text can express — and the other way around.
Structure you can actually follow
Repeats, branches, and splits render as framed containers holding their steps — nested, like the code — instead of a spaghetti of loop edges and reconverging arrows.
What you see is what you edit
The read-only view and the editor are the same canvas. No mental translation between the diagram in the dashboard and the thing you change.
Inspect any node
Click a send, a wait, or a branch to see its full configuration — the template, the topic, the SendQL condition — in a side panel.
Errors point at the source
Validation errors are anchored to source lines, so a broken condition shows up on the exact node it belongs to — on the graph and in the text.
Engine & safety
Built for the ways lifecycle email goes wrong
The scary part of automation isn't writing the flow. It's what the engine does at 3am with your whole contact list. Every guardrail here exists because of a specific failure mode.
Triggers
Enroll contacts on events, contact activity, segment entry, or dates like a renewal. Enrollment is forward-only by default — activating a workflow never mass-enrolls everyone who matched last year. Backfilling history is an explicit opt-in.
Waits & durations
Wait 2 days. Wait until a date. Or wait up to 7 days until a condition is met, with a timeout branch for when it isn't. Send windows keep emails inside business hours in each contact's timezone.
Frequency caps
An account-wide cap limits how many workflow emails any contact receives in a window, counted across all workflows. A time-sensitive flow can override it in its own .flow file — frequency cap 4 per 7 days, or off.
Manual send approval
Flip a workflow into approval mode and its sends queue for human review instead of going out. Useful for the first run of anything new, or any flow where a mistake is expensive.
Consent on every send
Consent is re-checked at the moment each email is sent, not at enrollment. A contact who unsubscribes on day 3 of a 14-day drip stops hearing from you on day 3.
Full run observability
A per-node funnel over the live canvas, a timeline for every contact's run, exit breakdowns by name, and a dry-run mode that walks a test contact through the flow on a virtual clock — before anything real is enrolled.
Agents
Your coding agent can run lifecycle marketing
SendFlow and SendQL are plain text with a published grammar. That's the whole trick: Claude Code or Cursor can author and maintain drip campaigns the same way they author and maintain code — and your PR review is the guardrail, exactly where it already is for everything else.
The agent drafts
“Write an onboarding drip: welcome email, a reminder if it goes unopened, tips on day 4, a nudge if they haven't logged in twice by day 11.” The agent writes the .flow file in a branch.
SendOps validates
Syntax, SendQL conditions, and the templates and segments the flow references are all checked. A broken condition fails validation — it never fails in a customer's inbox.
Your team reviews
The diff is a readable text file, not a screenshot of a canvas. Anyone on the team can see exactly what changed: one wait, one condition, one template swap.
Merge deploys
On merge, the workflow syncs to your account and goes live. Rollback is a git revert. The campaign's entire history is in your repo, next to the code it serves.
A click-ops journey builder can't be delegated — there's nothing for an agent to write, diff, or revert. Files can.
Two surfaces, one workflow
Because the canvas and the text are the same file, nobody is locked out. Marketers get a visual builder, developers get files in CI, and neither one overwrites the other's work.
For Marketers
Build and edit flows on the visual canvas — drag steps into place, set conditions, adjust waits. No code required, and nothing you build is trapped: every canvas edit is the same reviewable file underneath.
For Developers
Campaigns as code. .flow files live in your repo, validate in CI, and deploy on merge — the same pull-request workflow you already run for templates and everything else. Rollback is a revert.
For Founders
Set up the lifecycle once — welcome, onboarding, win-back — and let it run. Frequency caps and consent checks keep it polite while you're not looking, and the funnel view tells you if it's working.
Automation you can read, review, and own
Free plan available. Connects to your SES account in two minutes.