Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.brew.new/llms.txt

Use this file to discover all available pages before exploring further.

Brew’s event model has two directions — you push events INTO Brew today (inbound), and Brew pushes events back to your endpoints on the roadmap (outbound). This page describes what works today and what’s coming.

Direction map (TL;DR)

DirectionWho calls whoStatus
You → Brew — fire a custom event from your backend so Brew runs the matching automationPOST /v1/automation/runs { triggerEventId, payload, idempotencyKey }✅ Shipped
You → Brew — managed integration receivers (Clerk, Stripe, Shopify, Stytch, Supabase, WorkOS, RevenueCat) push their events INTO Brew through a Brew-hosted receive URL you configure oncePer-integration setup in the Integrations tab✅ Shipped
Brew → You — outbound webhooks for run / send / email-event lifecycle (run.completed, send.delivered, send.opened, …)Coming — see roadmap🚧 Planned
Brew → You — SSE / WebSocket streaming for live run logsComing with outbound webhooks🚧 Planned
Until outbound webhooks ship, the supported “wait for completion” pattern is polling — see Async jobs & polling.

Inbound — you call Brew

Pattern: fire a custom event

The canonical agent / backend integration path:
your-backend → POST /v1/triggers           → mint triggerEventId
your-backend → POST /v1/emails             → mint email bodies (emailType: 'automation')
your-backend → GET  /v1/domains            → pick verified sending domain
your-backend → POST /v1/automations        → assemble graph (trigger → sendEmail → wait → …)
your-backend → PATCH /v1/automations       → publish
your-backend → POST /v1/automation/runs    → fire whenever the real-world event happens
The fire call:
curl -X POST https://brew.new/api/v1/automation/runs \
  -H "Authorization: Bearer $BREW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: signup-jane@example.com-1779292800" \
  -d '{
    "triggerEventId": "tri_signup",
    "payload": { "email": "jane@example.com", "firstName": "Jane" }
  }'
# → 202 { success: true, status: "triggered",
#         details: { triggerInstanceId, automationRunIds: ["run_..."], ... } }
Key invariants:
  • payload.email is the contact key. Every trigger’s payloadSchema.fields MUST declare { key: 'email', type: 'string', required: true }. The fire branch also auto-upserts the contact derived from the resolved payload — declared firstName / lastName / subscribed land in core columns; everything else lands in customFields[key] (with the field definition auto-created on the brand).
  • Idempotency-Key is mandatory in practice. A doubled-fired webhook = doubled emails. See Idempotency.
  • The response is 202 with details.automationRunIds[]. Don’t block on delivery — poll the run with Async jobs & polling.

Pattern: integration-provisioned triggers

For events that originate inside another product (Clerk’s user.created, Stripe’s customer.subscription.deleted, Shopify’s orders/paid, etc.), Brew can RECEIVE the integration’s webhook directly — no code on your side. Setup is per-integration in the dashboard (the receiver URL is hosted at /api/integrations/<provider>/webhook/<connectionId>). Once configured, the integration’s events appear in GET /v1/triggers with provider: 'clerk' | 'stripe' | 'shopify' | … and fire your published automations exactly like custom brew_api triggers. You can bind any of these to an automation. Supported providers today:
ProviderCommon events
Clerkuser.created, user.updated, organization.created
Stripecustomer.subscription.created/updated/deleted, invoice.paid, customer.deleted
Shopifyorders/paid, customers/create, customers/update
Stytchuser.created, user.deleted, session.created
SupabaseINSERT, UPDATE, DELETE on configured tables
WorkOSdsync.user.created, dsync.user.updated, dsync.user.deleted
RevenueCatINITIAL_PURCHASE, RENEWAL, CANCELLATION, EXPIRATION
Each provider’s adapter normalises into a flat { email, …provider-specific fields } payload that flows through the same fire pipeline as brew_api triggers. Inbound integration webhooks also auto-upsert the contact using a richer per-provider mapper that lands under flat <provider>_* custom field names (e.g. clerk_user_id, stripe_customer_id).

Outbound webhooks (roadmap)

We’re shipping outbound webhooks so your backend can be notified when emails are delivered, opened, clicked, bounced, or marked as spam — and when automation runs complete or fail. Below is the planned contract; nothing here is live yet. Subscribe to the changelog for the launch announcement.

Planned event types

EventFires when
email.sentAn email is handed to the upstream ESP.
email.deliveredThe recipient’s MX accepted the message.
email.openedThe recipient opened the message (one event per open).
email.clickedThe recipient clicked a tracked link.
email.bouncedHard or soft bounce.
email.complainedThe recipient marked the message as spam.
email.unsubscribedThe recipient unsubscribed via the footer link.
send.queuedA POST /v1/sends campaign was accepted.
send.scheduledA scheduled send was queued for future delivery.
send.completedAll recipients of a send job were handled.
send.failedA send job failed to start (e.g. domain not ready).
run.startedAn automation run started a workflow.
run.completedA run finished all nodes successfully.
run.failedA run errored out at a node.
run.cancelledA run was cancelled (P7 — when cancel ships).
contact.created / contact.updated / contact.suppressedContact lifecycle events.
trigger.firedA trigger fire was accepted (before any per-automation routing).

Planned delivery contract

  • Signed. Every webhook will carry an HMAC signature header (X-Brew-Signature) and a timestamp (X-Brew-Timestamp) so you can verify origin.
  • Retried. Exponential back-off (1s / 5s / 30s / 5m / 1h / 6h) until a 2xx response, then dropped after 24h.
  • Per-subscription delivery logs. Inspect deliveries + replay failed ones from the dashboard.
  • Idempotent. Every event carries a unique event.id; consumers should dedupe.

Planned subscription management API

POST   /v1/webhook-subscriptions     { url, events: [...], description? }
GET    /v1/webhook-subscriptions
PATCH  /v1/webhook-subscriptions     { subscriptionId, url?, events?, enabled? }
DELETE /v1/webhook-subscriptions     { subscriptionId }
POST   /v1/webhook-subscriptions/rotate-secret { subscriptionId }
GET    /v1/webhook-subscriptions/deliveries?subscriptionId=…  // delivery log

Planned envelope

{
  "id": "evt_01HZ…",
  "type": "send.delivered",
  "createdAt": "2026-04-08T12:34:56.789Z",
  "data": {
    "sendId": "snd_…",
    "emailId": "eml_promo",
    "recipientEmail": "jane@example.com",
    "messageId": "<resend-msg-id>"
  }
}

What to do today

NeedToday’s pattern
React to inbound events (signup, purchase, churn) and run an automationPOST /v1/automation/runs (fire) — works now
Wait for a fire to completePoll GET /v1/automation/runs?automationRunId=… — see Async jobs
Receive provider webhooks (Stripe, Clerk, Shopify, …) into BrewConfigure the integration in the dashboard; Brew hosts the receive URL
Notify your backend when an email opens / deliversRoadmap — outbound webhooks. Until they ship, use the dashboard analytics, or the ESP’s own webhook (Resend) if you have direct access
Stream live run logs into a UIRoadmap — SSE. Today: poll GET /v1/automation/runs?automationRunId=…&include=logs

Security checklist (will apply when outbound webhooks ship)

  • Verify the HMAC signature on every webhook (header X-Brew-Signature).
  • Treat the timestamp as a freshness check — reject deliveries older than 5 minutes.
  • Dedupe on event.id — Brew may retry on 2xx slowness or your own brief outage.
  • Return 2xx only when you’ve persisted enough state to survive a restart; return 4xx for permanent rejections so we don’t retry.

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.