Split transactional and marketing email into separate streams
A password reset and a Friday promotion have nothing in common except your sending reputation when they leave through one undifferentiated queue. A complaint spike on the promotion then lands your resets in the spam folder, and the resets are exactly the mail you cannot afford to lose. Email streams fix this by partitioning your org’s sending into named streams. Each stream carries its own reputation class (transactional,
marketing, or custom), its own throttle ceilings, and its own roster of
senders, so one kind of traffic cannot consume or damage another. If you
send both classes from the same org, this split is the single biggest
deliverability move available to a new sender: do it before warm-up ramps,
before volume, before anything else.
You will:
- Understand what a stream isolates
- Create the transactional and marketing streams
- Set the default stream
- Bind senders to a stream and verify sends
- Tune per-stream throttles
- Read per-stream delivery and suppression health
- Migrate existing senders without a big bang
- Know when streams are enough and when to add a dedicated IP
1. What a stream isolates
Every stream you create gives you three separations:- Reputation surface. Mailbox providers grade the traffic they see. Bulk marketing mail legitimately runs at higher complaint rates than transactional mail; on a shared identity, those complaints are counted against your receipts too. On separate streams, each class is graded on its own behaviour.
- Throttle ceilings. Each stream carries its own
maxPerMinuteanddailyQuota, so the blast class’s pacing is set and read independently of the transactional class’s configuration. - Sender roster. A sender belongs to exactly one stream, so what each class sends, and how it performs, is readable per stream instead of blended into one org-wide average.
kind field is a closed set: transactional, marketing, or
custom. Use custom for a sub-brand or a one-off broadcast class that
deserves its own reputation surface.
2. Create the transactional and marketing streams
Create both streams withPOST /email/streams. Only name is required
(1 to 80 characters); kind defaults to transactional when omitted.
throttle.maxPerMinute and throttle.dailyQuota are positive integers, or
null for no per-stream cap (the stream then inherits the org-level send
policy). Save both estream_... ids; every step below references them.
Two guardrails to know: stream names are unique per org
(case-insensitive), so a duplicate name returns 409 and you should edit
the existing stream instead; and GET /email/streams lists every stream
with its current throttle and default flag at any time.
3. Set the default stream
The org always has exactly one default stream; the platform maintains that invariant on every write. The default is where any send resolves when its sender carries no explicit stream assignment, so make the transactional stream the default: untagged traffic should always take the safe path.isDefault: true and the
previous default demoted. Calling set-default on the current default is a
no-op, so it is safe to run in automation.
Resolution is deterministic: a send takes the stream explicitly assigned
to its sender, otherwise the default stream, and an org with no streams
configured sends exactly as it did before this feature. You can also
promote at create or update time with "isDefault": true.
4. Bind senders to a stream and verify sends
Streams carry no mail by themselves; senders do. Register a sender against a stream withstreamId at creation, or re-assign an existing sender with a
PUT:
streamId that does not exist is rejected with 400 before
anything is persisted, so a typo can never leave a sender pointing at a
ghost stream. Send "streamId": null to clear an assignment; the sender
keeps sending and falls back to the default stream.
Templates do not carry a stream; the send’s sender does. Render a template
as usual and dispatch it from the from address of a sender bound to the
right stream.
A send returns the same 202 envelope regardless of stream, with
data.succeeded rows and meta.request_id:
GET /email/senders: each sender row carries its streamId, and the
from address on the send resolves to that sender. Correlate the
meta.request_id with your webhook events to follow the message through
delivery on that stream’s traffic.
5. Tune per-stream throttles
Throttles are edited in place withPUT /email/streams/:streamId:
- Omitting
throttleleaves the existing ceilings unchanged. - Sending an explicit empty object
{ "throttle": {} }clears both ceilings back tonull(no per-stream cap). - Each ceiling is independent: set
maxPerMinutealone anddailyQuotastays as it was.
6. Read per-stream delivery and suppression health
Because a sender belongs to exactly one stream, per-stream health is the roll-up of that stream’s senders. Two surfaces cover it:- Webhooks. Subscribe
message.delivered,message.failed,email.opened, andemail.clickedas in Send your first transactional email, and group events by the sending address. Each address maps to one sender, each sender to one stream, so delivery, bounce, and complaint rates split cleanly per stream. - The dashboard. Channels → Email shows delivery and engagement for your sends, and the Sent Emails log carries the sending address on every row, so you can read each stream’s traffic the same way.
7. Migrate existing senders without a big bang
If you already send both classes through one undifferentiated setup, move in steps rather than flipping everything at once:- Audit. List your senders (
GET /email/senders) and label each one by what it actually sends: transactional, marketing, or mixed. - Create the streams and set the default. Follow steps 2 and 3, with the transactional stream as default so any sender you miss keeps resolving to the safe path.
- Move clean senders first. Senders that are purely transactional or
purely marketing get their
streamIdset immediately withPUT. - Split mixed senders by percentage. For a sender that sends both,
register a second sender address for one class (for example
news@yourdomain.comfor marketing), point a slice of that traffic at it, and raise the share over one to two weeks while you watch both streams’ delivery and complaint deltas. - Compare deltas. Delivery rate and complaint rate per stream, before and after each move. The transactional stream’s numbers should stay flat or improve as marketing volume leaves it.