SendLang: A DSL for Email Automation SendLang introduces two domain-specific languages, SendQL and SendFlow, for email lifecycle automation, enabling version control, type-checked queries, and agent-driven campaign creation. The languages allow developers to define email segments and workflows as plain text files that can be diffed, reviewed, and iterated on by coding agents, reducing errors and improving collaboration. A language layer for lifecycle email SendLang is the home of two small, focused languages. SendQL describes who a message is for. SendFlow describes what happens to them over time — and speaks SendQL natively. Written by people and by coding agents — a published grammar, a type checker to iterate against, and a diff you approve. See how agents . SendLang One family, two languages SendQL is the noun. SendFlow is the verb. Together they turn contact data and event streams into campaigns you can read in a diff. SendQL the whoA segment-definition query language. Every query is a single boolean expression describing which contacts a segment matches. SendFlow the whatA drip-workflow language stored in .flow files. It reads top to bottom and embeds SendQL for every condition. Why a language Your lifecycle email, in version control Every other tool locks your cohorts and lifecycle campaigns inside a visual editor. Make them a language instead, and they become text you can diff, review, and hand to a coding agent. - It lives in version control - Every segment and workflow is a plain text file. Diff it, review it in a pull request, roll it back. Your targeting and your drip logic earn the same rigor as the rest of your code. - It fails before it sends - A misspelled attribute is a type error with a line and a column — not a campaign that quietly sends to nobody for a week. - A coding agent can write it - This is the big one. A canvas is a dead end for an agent; a language is the loop it already works in. Describe the outcome, let it draft the file, merge it once it passes review. Coding agents Hand the campaign to an agent A canvas is a dead end for a coding agent — it cannot read it, change it, or tell you what it changed. A language is the loop an agent already works in: read the file, write the file, run the checker, open a pull request. That is not a side effect of the design. It is a reason for it. “Three days before a trial ends, email them. If they have not clicked within three days, send the upgrade offer.” - The docs are machine-readable - Every page has a Markdown twin, and llms.txt / llms-full.txt hand an agent the whole of both languages in a single fetch. Nothing to scrape, nothing to guess at. - A grammar, not a guess - The normative EBNF and the reserved-word list are emitted by the parsers themselves, so the spec an agent reads cannot drift from the code that judges what it writes. - The type checker is the feedback loop - Every parse and type error carries a line, a column, and a message that names the fix. The agent iterates against the checker until the run is clean — and nothing sends until it is. - You are still the approver - No goto, no unbounded loops, nothing to evaluate. The worst an agent can hand you is a workflow that terminates — and you read it as a diff before it ever reaches a contact. The shortest useful thing you can do: put /llms-full.txt /llms-full.txt in your agent's context. That is both languages, complete, in one fetch. Writing SendLang with a coding agent /docs/agents SendQL Describe exactly who you mean SendQL reads events as a first-class source alongside contact attributes — the gap left by engines that see only precomputed rollups, never the raw stream. - Attributes and lists - Match on contact data with operators, in ... sets, contains / starts with / ends with, and list or segment membership. - Events as a first-class source - Query the raw event stream directly — count, exists, sum, avg, last, first — not just precomputed engagement rollups. - Consent and suppression - Express deliverability rules inline: subscribed to "topic", opted out of "topic", unsubscribed from all, not suppressed. - Windows and recency - Bind a time window to any event source with within 30d or between two dates, and reason about recency with now - 14d. The same file, rendered as a canvas - Entercart updated - Wait1 hour - Sendcart-reminder - repeat up to 2 · every 24h · until orderSendcart-nudge - Exitpurchased SendFlow Structure, no goto A workflow reads top to bottom and maps losslessly to a flowchart — sequence, waits, branches, weighted splits, and one bounded repeat. Never a goto, never an unbounded loop. - One trigger, many exits - Enter on a segment, an activity, an email event, or a duration before a contact's own date. Named exits report conversions the moment they happen. - Timed steps - wait 2d, wait until a date or attribute, or wait up to 7d until a condition — with an optional timeout arm. - Branch, split, hold out - Fall-through if / else if / else, weighted split { 30%: ... 70%: ... } that must sum to 100, and hold out 10%. - Bounded loops only - The single loop is repeat up to N every duration until a condition. No goto, no unbounded loops, ever. How they fit SendFlow speaks SendQL The predicate you write to define a segment is the very same syntax that drives every condition inside a workflow. A SendQL segment The same predicate, inside a workflow - A strict superset - One shared token stream - One type-checker and diagnostics Design Built like languages, not config Both languages are versioned by a profile number, split cleanly from their execution engines, and specified down to a machine-checked grammar. - Structured by design - Sequence, branch, bounded repeat, timed waits, named exits — a control-flow subset that maps losslessly to a flowchart. - Events as first class - Selection over the raw event stream sits alongside contact attributes, so you can segment on behavior, not just state. - Golden-pinned grammar - A normative EBNF is generated from the parser and pinned by tests, so the spec can never silently drift from the code. - Precise diagnostics - Parsing and type-checking are separate stages, and every error carries a line and column pointing at the exact token. - A canonical formatter - SendFlow ships a one-true-layout printer — gofmt for flows — so every workflow in your repository reads the same way. - A canvas, losslessly - The control flow is restricted enough that a workflow maps onto a flowchart and back with nothing lost — comments included. Text and canvas are two views of one thing. Documentation The whole language, written down Every construct in both languages, every diagnostic they can produce, and a normative grammar generated from the parsers themselves — so the spec can never quietly drift from the code.