Skip to main content
Brew’s two delivery primitives (campaign sends and automation runs) are asynchronous by design. The HTTP call accepts the work, persists an audit row, and returns a job identifier; the actual delivery happens on a durable Vercel Workflow runtime that retries, backs off, and emits per-step logs. This page covers the three async primitives, the canonical poll loop, and the agent-friendly recipes for concurrency.
Outbound webhooks for run / send / email-event lifecycle are on the roadmap. Until they ship, polling is the supported pattern. Both surfaces work today.

The Three Async Primitives

A POST /v1/emails (AI generation) is also long-running (~30-90s) but blocks the HTTP call until the agent renders the design or refuses. It is not a job-returning async. You get the artifact back in the response.

Fire a Trigger, Then Poll the Run

Fire returns a list of automationRunIds (one per matched Published automation). Each id is a workflow run you can inspect. If no bound automation is Published, the call returns 422 NO_PUBLISHED_AUTOMATION instead of 202 and there is nothing to poll. Publish at least one bound automation and re-fire.
The automation.runs.read policy is 100/min per key. A 2-second poll is well under that for a handful of concurrent agents.

Queue a Send, Then Verify

POST /v1/sends returns 202 { status, sendId, runId, scheduledAt? }. The sendId keys the send row for analytics reads; the runId is the Vercel Workflow run id (correlated with delivery analytics on the dashboard). The body takes EITHER an audienceId OR an inline to (a single email or an array of ≤ 50).
Poll the send row with GET /v1/analytics/sends?sendId=…; add &include=events to inline the per-recipient feed (delivered / opened / clicked / bounced) on the same row. For one-shot validations, the dashboard at brew.new/analytics is also a source of truth.

Idempotency on Async Retries

Because retries are guaranteed-safe with idempotency keys, your agent / cron loop can re-fire the same operation without double-delivery. Same key + same body → same automationRunIds[], same runId, same response payload. See Idempotency for the full contract.

Agent Concurrency Patterns

Pattern 1: Fan Out, Fan In

An agent that runs 50 different welcome flows in parallel:
The automation.runs.write policy is 60/min. For 50 parallel fires, you’re well under; for 200+ you’d batch with Promise.allSettled + a 429-aware retry helper.

Pattern 2: Long-Poll a Single Run to Terminal

Use a generator so the agent can yield other work between polls:

Pattern 3: Bulk Inspect by Automation

For dashboards / agents that want “the last 100 runs of this automation”:

Why No Webhooks Today (and What’s Next)

The fastest path to an A-grade Events & Streaming score is outbound webhooks; we’re shipping them. See the Webhooks & events page for the planned contract (webhook.run.completed, webhook.send.delivered, webhook.send.opened, …) and the roadmap. Until then, the poll loop on /v1/automations/runs is the supported way to wait for terminal status.

Endpoints That Are Already Async-Friendly

See Also

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.

ChatGPT/Claude Integration

Click “Open in ChatGPT” at the top right of any page to analyze documentation with ChatGPT or Claude for deeper insights.