Skip to main content
A send is the unit of delivery and analytics in Brew. Every production send with POST /v1/sends returns a sendId for lifecycle status and aggregate stats. Test sends return a completed status and recipient without a send row. Sends read at their own root: the list is /v1/sends, and one send is a path read.
  • GET /v1/sends: list every campaign send for the brand, with stats.
  • GET /v1/sends/{sendId}: one send’s lifecycle status + stats, as a bare row.
  • GET /v1/sends/{sendId}?include=events: the same row with its per-recipient event feed inlined.
  • GET /v1/sends?emailId=…: every send for one design.
These reads use the sends scope (which emails implies), on the sends.read rate-limit policy. POST /v1/sends is polymorphic by test and uses sends.write.
GET /v1/analytics/sends and GET /v1/analytics/campaigns are gone. So is the old habit of listing with ?sendId= and taking data[0]: that query key now returns 400 INVALID_REQUEST. See Migrating to the cleaned-up v1 surface.

List Recent Sends

The response is the uniform list envelope: { data, pagination }. See Pagination for the cursor loop, or use the SDK auto-pager:
A status filter accepts the one send vocabulary: scheduled, queued, running, paused, completed, partially_completed, failed, canceled. The older sent / sending / partially_sent spellings are rejected as 400 INVALID_REQUEST.

Poll a Single Send

Read one send by id to watch it move from queued to running to completed and to read its rolled-up stats. The response is the send row itself, with no data wrapper. An unknown or cross-brand id returns 404 SEND_NOT_FOUND.

Read the Per-Recipient Feed

Once a send is in flight, drill into its individual delivery events: sent / delivered / opened / clicked / bounced / complained / unsubscribed. Add ?include=events to the single-send read to inline a bounded first page of the feed on the same row. For the full, filterable history, use GET /v1/analytics/events with the same sendId (see Explore per-contact engagement).

Join an Automation Run to Its Deliveries

An automation send is one row per recipient, so it is never listed brand-wide. Pass a join filter instead and kind=automation is implied: automationId, automationRunId, audienceRunId, or triggerInstanceId.
Naming a run outside the brand returns 404 AUTOMATION_RUN_NOT_FOUND or 404 AUDIENCE_RUN_NOT_FOUND. Combining a join filter with kind=campaign or with emailId, or asking for kind=automation without one, returns 400 INVALID_REQUEST.

Pause, Resume, or Cancel a Send

Lifecycle changes are action sub-paths, not a body verb: A paused send carries pauseReason. manual means someone paused it; resume it to continue. plan_limit means the organization ran out of monthly email sends. That send resumes on its own once the allowance has room, so resume refuses it; cancel it if it should not go out.

Send a Test Email

The same POST /v1/sends endpoint sends a test when you pass test: true. A test send forces the Brew default sender unless you name a verified org-owned domainId, requires no audience, does not write a send row, and resolves synchronously to 200 { status: 'completed', recipient }.
The live (campaign) send is the same endpoint without test. It returns 202 { status: 'queued' | 'scheduled' | 'pending_approval', sendId, scheduledAt? }. pending_approval waits for an organization administrator to approve the send. There is no runId: sendId is the only handle you need. Name the sender with from: { email, name? } and put replyTo at the top level. See Async jobs & polling.

Need Help?

Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:

Search Documentation

Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.

Ask ChatGPT or Claude

Click here to open the Copy page menu at the top of this page, then send the page to ChatGPT, Claude, or your editor.