PosthasteDocsGet an API key

Reference

Message streams

Somebody who reports your newsletter as spam has said something about newsletters. Stopping their password reset because of it is a worse failure than the complaint. A stream is that separation.

Every account starts with one

Your account has a transactional stream from the moment it exists, and a send that names no stream lands there. You can create others and name one per send.

An unknown stream is refused, never defaulted. Naming a stream tells us which suppression list applies to the send. Quietly using a different one because the slug was mistyped is how a complaint stops protecting anybody.

What a stream does and does not escape

This is the part worth reading twice. Which suppressions a stream escapes is decided by the reason, not by the stream:

ReasonApplies toWhy
complaint, unsubscribeThe stream that earned itThey are statements about a kind of mail. This is the point of the feature.
hard_bounce, spam_trapEvery stream, alwaysA dead mailbox is a fact about the address. Continuing to send to it from another stream is the fastest way to lose sending reputation.
manualYour choice; every stream by default“Stop sending to this person” usually means all of it, so that is the safer reading.

A stream is not a way around the suppression list. Ask us to confine a hard bounce to one stream and we will store it account-wide anyway. Never sending to a suppressed address is the rule this platform is built on, and a feature that punched a hole in it would not be worth having.

Endpoints

GET/v1/streamsstreams:read
POST/v1/streamsstreams:write

A slug is lowercase letters, digits and single hyphens — it goes into your code, so it has to stay typeable and stay stable. transactional is reserved.

Filter the message log by stream with ?stream=product-updates.

How many streams you may have is set by your plan, and the transactional stream counts: Free 1, Starter 3, Growth 10, Scale 25, Enterprise unlimited. So a plan listing one stream is a plan on which this endpoint refuses, with 403 and "type": "stream_limit_reached". The full grid is in limits.

Examples

POST /v1/streams
{ "slug": "product-updates", "name": "Product updates" }

201 {
  "id": "str_AZLm3kQ8T2Sf9pXbNc7HrQ",
  "slug": "product-updates",
  "name": "Product updates",
  "isDefault": false
}

NextTags and metadataLabel a message with your own vocabulary, get it back on the log and every webhook, and filter by it.