PosthasteDocsGet an API key

Reference

Message streams

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

Every account starts with two

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.

It also has announcements, which is where broadcasts go by default. A broadcast can never use the transactional stream — the API refuses it with 422 transactional_stream, and so does the database — because an unsubscribe from an outage notice must not stop the same person’s password resets. announcements does not count against your plan’s stream limit.

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 and announcements are 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 — announcements does not: 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
}

NextContacts and lists →An address book of your own users, for operational announcements. Every contact records where consent came from, and whether it can be mailed is the suppression list’s answer.