Skip to the documentation
API

List the sending lanes

The two lanes mail travels in, with their bounce and complaint numbers.

GET/sending/streams

Runs any of 2 calls on your workspace.

GET /sending/streams

The two lanes mail travels in, with their bounce and complaint numbers.

Example

Needs emails:read. Mail travels in two lanes with their own queues and reputation: transactional for app mail such as password resets and receipts, and broadcast for newsletters. Each lane comes with its numbers for the last 7 days and the limits the broadcast lane is held to.

curl
curl "$OE/sending/streams" -H "$AUTH"
Response
{ "object": "list", "data": [{  "object": "sending_stream",  "stream": "broadcast",  "status": "active",  "pausedAt": null,  "pausedReason": null,  "resumedAt": null,  "window": {    "since": "2026-10-04T09:41:00.000Z",    "until": "2026-10-11T09:41:00.000Z",    "recipients": 18240,    "bounced": 212,    "complained": 9,    "bounceRate": 0.0116,    "complaintRate": 0.0005  },  "limits": { "bounceRate": 0.04, "complaintRate": 0.002, "minimumRecipients": 500, "windowDays": 7 }}] }

The broadcast lane pauses itself when its bounce rate passes 4% or its complaint rate passes 0.2% over those 7 days, once it reached minimumRecipients. pausedReason says which, the workspace owner gets an email, and app mail keeps going out.

While it is paused, a message sent with stream set to broadcast and a new broadcast are a 409 stream_paused, and broadcasts already going out are held.

Choose a lane

Needs emails:send. stream on POST /emails, on each message of POST /emails/batch and on POST /templates/{id}/send chooses the lane. It is transactional unless you say otherwise, and every copy of a broadcast travels on broadcast.

curl
curl -X POST "$OE/emails" -H "$AUTH" -H "Content-Type: application/json" \  -d '{ "from": "[email protected]", "to": "[email protected]", "subject": "What is new in October", "text": "Three things shipped this month.", "stream": "broadcast" }'
Response
{ "object": "email", "id": "msg_5f1c9a0e7b2d4c6a8e3f1b7d", "status": "queued", "stream": "broadcast" }

Over SMTP, the header X-OpenEmail-Stream: broadcast does the same, and it is removed before the message leaves.

Resume a paused lane

Needs domains:write and the workspace owner. POST /sending/streams/broadcast/resume resumes the lane: held messages go out at once and the 7 day count starts again from now, so clean the audience first.

curl
curl -X POST "$OE/sending/streams/broadcast/resume" -H "$AUTH"
Response
{  "object": "sending_stream",  "stream": "broadcast",  "status": "active",  "pausedAt": null,  "pausedReason": null,  "resumedAt": null,  "window": {    "since": "2026-10-04T09:41:00.000Z",    "until": "2026-10-11T09:41:00.000Z",    "recipients": 18240,    "bounced": 212,    "complained": 9,    "bounceRate": 0.0116,    "complaintRate": 0.0005  },  "limits": { "bounceRate": 0.04, "complaintRate": 0.002, "minimumRecipients": 500, "windowDays": 7 }}

A lane that is not paused is a 409 stream_not_paused.

An OAuth access token needs a verification code for this call. Until the app has verified one in the last 60 minutes, the call answers 403 step_up_required and changes nothing. An API key is never asked. The Authentication page shows how to ask for a code and verify it.

Reference