Deliverability
A newsletter can never break a password reset.
Every rule here exists because of a specific way mail stops arriving.
Two identities per domain so reputations cannot bleed. Suppression with a scope so an opt-out blocks the right mail and only that. A gate that refuses the campaign that would get the account reviewed.
{ "domain": "acme.com" }{ "data": [
{ "domain": "mail.acme.com",
"risk_class": "transactional",
"status": "pending",
"dns_records": [ … ] },
{ "domain": "news.acme.com",
"risk_class": "marketing",
"status": "pending",
"dns_records": [ … ] }
] }Two identities
Mailbox providers score the From domain. So there are two.
mail.acme.com transactional receipts, resets, replies
news.acme.com marketing campaigns, sequences
# routed by the risk class of each message
POST /v1/emails
{ "from": "Acme <hi@mail.acme.com>", … }
→ "risk_class": "transactional"
{ "from": "Acme <hello@news.acme.com>",
"risk_class": "marketing", … }
# a campaign from the wrong side
422 { "error": { "type": "no_verified_identity" } }Reputation is scored per sending domain. If transactional and marketing mail share one, a campaign with a complaint spike takes the password resets down with it, and that is the failure this split prevents.
mail. and news. are separate identities with separate reputations all the way down: separate DNS records, separate sending configuration in the layer underneath, separate metrics.
risk_class is a required concept, not a tag.
Every message is one or the other, the sending identity is resolved from it, and broadcasts and automations only send as marketing. A sequence that tried to go out from mail. is refused when it is defined, not discovered as a failed enrolment later.
Marketing mail also carries a one-click unsubscribe header and a footer with the sender’s postal address. A marketing send without an address on the workspace is refused with no_postal_address, because made optional it is the field nobody fills in.
Suppression
An opt-out has a scope. A bounce does not.
GET /v1/suppressions
{ "data": [
{ "email": "maya@acme.com",
"reason": "unsubscribe",
"scope": "marketing" },
{ "email": "old@acme.com",
"reason": "hard_bounce",
"scope": "all",
"detail": "550 5.1.1 user unknown" },
{ "email": "angry@acme.com",
"reason": "complaint",
"scope": "all" }
] }
# a receipt to maya still goes out
POST /v1/emails { "to": "maya@acme.com", … }
202 { "status": "sent" }
# a newsletter to her does not
202 { "skipped": true,
"reason": "suppressed:unsubscribe" }A marketing unsubscribe never blocks transactional mail.
Someone who leaves the newsletter still gets their invoice and their password reset. An opt-out is scoped to marketing; only hard bounces and complaints get scope: "all", because those addresses should get nothing.
Transient bounces are not suppressed.
A full mailbox or greylisting resolves on its own. Suppressing it silently shrinks a healthy list, one out-of-office at a time. Only a permanent bounce writes a suppression.
A complaint always suppresses everything.
Someone pressing spam is the signal mailbox providers judge a domain on. That address is not mailed again, from either identity.
A suppressed send is recorded, not dropped.
It comes back 202 with skipped: true and the reason, and it appears in the log, so support can answer why something did not arrive.
Every send checks the list because every send goes through one function. A caller that could bypass it would mail people who opted out or bounced, which is how an account gets its sending paused. Removing a suppression is possible, and it is never the fix for a failed send.
Unsubscribe
Unsubscribe is two writes.
# one-click, from the header or the footer
POST /api/unsubscribe?token=…
1. suppress maya@acme.com scope: marketing
2. cancel every scheduled send to her
and end every automation enrolment
# what a single write looks like from her side
day 0 welcome (opened, unsubscribed)
day 3 three things ← still arrives
day 10 how is it going ← still arrivesSuppressing the address stops new sends. It does nothing about the ones already queued.
A welcome sequence is three or four messages scheduled days apart, and if the opt-out only writes the suppression, days three, six and ten still arrive. To the person it looks like an unsubscribe that did not work, and the next click is the spam button.
So the unsubscribe path does both: it suppresses, and it cancels every pending scheduled send for that recipient.
Because automations generate their next step live, it also ends the enrolment, or the sequence would just produce the next one. Opting down from a single topic does neither; the person still wants some of your mail, and a suppression row would silently block it.
Unsubscribe tokens are scoped to the workspace as well as the address, so a link from one tenant cannot opt someone out of another.
It says no
Refused before it starts. Paced once it does.
GET /v1/broadcasts/:id/preview
The reputation gate: bounce and complaint rates over the last 14 days, checked against ceilings set short of the thresholds that get an account reviewed. A campaign that would cross them is refused with blocked_reason, and the check runs again in the send path just before the fan-out. Campaigns.
50 recipients per call, N per second
The layer underneath accepts fifty destinations per bulk call and enforces a per-second send rate. Everything that fans out is chunked to that size and paced against the account’s rate on purpose.
Ignoring the pacer turns a healthy campaign into thousands of throttle retries; pacing it turns a large list into a long, quiet run.
suppressed before, not during
A fan-out resolves the suppression list once for the whole run, not per recipient, and the count in the preview is already net of it.
workspace_suspended
Every tenant shares the reputation of the layer underneath, so one tenant’s bounce rate can get sending paused for all of them.
Staff can suspend a workspace, and the check sits at the last point before dispatch, so it also halts scheduled mail and a campaign already fanning out.
Warm-up
A new domain starts at 200 a day, whoever asks.
# day one of news.acme.com, 18,000 recipients
POST /v1/broadcasts/bc_.../send
sent 200 the day's whole allowance
status paused
reason Warm-up limit for news.acme.com reached
for today. Sending continues tomorrow.
day 1 200 day 5 5,000
day 2 500 day 6 10,000 <- 18,000 drained
day 3 1,000 ...
day 4 2,500 day 14 750,000 then uncappedA domain nobody has received mail from has no reputation to spend.
A day-old identity emitting eighteen thousand messages at one provider is indistinguishable from a spam cannon, and it is the one deliverability mistake with no cheap recovery: reputation is earned over weeks and lost in an afternoon.
So every new sending identity gets a daily ceiling on marketing volume for its first fortnight, and the ramp is anchored on its first send rather than the day it verified. A domain verified a month ago and never used is exactly as cold as one verified this morning, because mailbox providers judge sending history, not DNS records.
A campaign that runs out of the day’s allowance is not refused.
It pauses with the remainder pending and resumes tomorrow at the next step, so one large send spreads across days — which is what a warm-up is.
Transactional mail is never capped: a password reset cannot wait for tomorrow’s step, and it leaves from the other identity with its own reputation.
The incident that produced this: 18,113 messages in twenty-nine minutes, and what Gmail did about it.
Opens and clicks
Tracked on your domain, or not at all.
link.mail.example.com CNAME
One optional record per sending subdomain. Publish it and every web link in mail from that subdomain is rewritten to a signed path on that hostname; the click is recorded and the recipient is redirected. The link a person hovers over carries your name from start to finish.
Nothing goes through a SendRaven domain, and nothing goes through the sending substrate’s tracking domain, which is the one that shows up in a spam filter’s domain-mismatch check.
link.news. and link.mail., separately
A tracking domain earns a reputation of its own with the link blocklists. One shared between campaigns and receipts would reconnect the two things the domain split keeps apart, so each subdomain has its own, and a sender who only wants click data on campaigns publishes only the news. record.
data-track="off"
A bare URL in the HTML is turned into a link and tracked; this attribute on an anchor leaves that one link alone. The unsubscribe link is never rewritten, and HEAD requests redirect without counting, which filters the commonest link scanners.
open, click, replied
The email page shows each open and click beside delivery, and each reply with a link through to the conversation. Opens need no setup; a pixel carries no domain a filter would object to.
Questions
How do you stop marketing email from hurting transactional deliverability?
By never sending them from the same domain. Mailbox providers score the From domain, so marketing goes out on news. and transactional on mail., with separate configuration sets — a complaint spike on one cannot reach the other.
How do I warm up a new sending domain?
You do not have to: every new sending identity is capped automatically, starting at 200 marketing messages on its first day and roughly doubling for a fortnight. The ramp is anchored on the first send rather than on DNS verification, because providers judge sending history, not records, and a campaign that exceeds the day's allowance pauses and resumes the next day instead of failing.
Does an unsubscribe stop emails that are already scheduled?
Yes. Unsubscribing is two writes: it suppresses the address and cancels their pending scheduled sends, because doing only the first means days 3, 6 and 10 of a welcome sequence still arrive after somebody opted out.

The checks sit in the send path, not on a dashboard.
Verify a domain and both identities come with it. Read the quickstart, subscribe to bounce and complaint events, or see what else the same key covers.

