Cold email infrastructure that owns its data supply.
outreachstack builds lead sets by querying a 343M-domain feed it ingests daily, generates and then verifies every DNS record a sending domain needs, and sends on a scheduler that halts the sequence the moment someone replies. One /api/v1 surface — every operation is an HTTP call, and every lead carries its provenance.
curl -sX POST /api/v1/icps/draft-from-url✓ criteria drafted — versioned, yours to edit firstcurl -sX POST /api/v1/icps/estimate✓ audience sized — costs nothing, commits nothingcurl -sX POST /api/v1/leads/build✓ leads promoted — deduped, provenance + lawful basiscurl -s /api/v1/sending-domains/$ID/records✓ MX, SPF, DKIM, DMARC — each with what breakscurl -sX POST /api/v1/campaigns/$ID/launch✓ dry run by default — a reply halts the sequence// publishing those DNS records is still yours.
Four stages, one system. What each stage emits is what the next one queries.
A domain lake we ingest, not a list we rent
A daily bulk delta lands in ClickHouse, and MX, TXT, SPF and CNAME records are resolved into signals before anyone is contacted. The distinction that matters: an SPF `include:shops.shopify.com` proves a company *sends through* Shopify, while a CNAME to `myshopify.com` proves the storefront *runs on* it — and only the second can install your app. An ICP is a versioned query over that, not a CSV someone else already sold four times.
Every record a sending domain needs, generated then verified
The API emits the exact MX, SPF, DKIM and DMARC records for a domain — each one carrying what breaks without it — then checks them from public DNS and reports the value it actually observed. Not a boolean: `present but wrong` is the common case and the dangerous one. The SPF evaluator is a real RFC 7208 implementation, so it counts the ten-lookup limit across the whole expansion tree instead of guessing. Publishing the records is still yours to do; automating that is next.
Copy a model writes, on a scheduler that will not misfire
Copy is generated per campaign from the ICP and the offer, and every model call is retained, so any line traces back to the request that produced it. Sends are claimed out of Postgres under a per-mailbox daily cap inside the recipient's own business hours — and one reply halts every pending step for that person, across every campaign, not just the one they answered.
Bounces and complaints, handled the moment they land
The MTA posts bounces and complaints back as webhooks; they are classified, attributed to the mailbox and domain that produced them, and the address goes onto a suppression list every later send is checked against. New mailboxes climb a warmup ramp and a campaign cannot launch past a mailbox that has not started one. The control loop that throttles, rotates and pauses on its own is documented and not yet built — a human runs those runbooks today.
What runs, and what doesn't yet- daily cap
- sends
Tenant #1 is us, and the first send went to spam.
outreachstack.org is a real sending domain on our own Postal MTA and our own IPs — deliberately separate from the brand domain you're reading this on, so a reputation problem on one can never touch the other. It authenticates cleanly: spf=pass dkim=pass dmarc=pass, mail-tester at 9/10. Then the first live message to a Gmail account was filed as spam anyway, which is the lesson a spec sheet will not teach you and which is written up in our own deliverability runbook. We have no customer logos to show you; we're pre-launch, and this page will say so until it isn't true.
Every operation is an HTTP call — the dashboard is one client of it, not the product.
Drafting an ICP from a competitor's URL, sizing the audience before you pay to build it, generating a sequence's copy, connecting a mailbox, launching a campaign — none of it exists only behind a click. An autonomous agent and the dashboard sit on the same /api/v1 surface, authenticated with the same scoped, tenant-bound keys and subject to the same row-level tenant isolation as a human operator.
The dashboard, for operators who want to look at the data.
A typed client over the same API. Commands whose endpoint isn't wired yet exit `4` and name the endpoint they are waiting on, rather than faking a success.
Ordinary API consumers with scoped keys — no privileged back door. Anything that spends money or sends mail takes `dry_run=true`, and long jobs return an envelope to poll.