Skip to main content
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)

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:
The fire call:
Key invariants:
  • payload.email is the contact key. Every trigger’s payloadSchema.fields MUST declare { key: 'email', type: 'string', required: true }. The fire endpoint also upserts the contact from the declared top-level scalar fields the fire sent: firstName / lastName / subscribed land in core columns, and the rest land in customFields[key] (with the field definition auto-created on the brand). Object and array fields are not stored on the contact; they stay available to the email as {{ trigger.account.tier }}. A field’s fallbackValue fills the email only and is never written onto the contact (a subscribed fallback only sets the subscription of a new contact). A value the contact field’s type refuses, such as "abc" for a number field, is not saved: the rest of the contact is, the automation still runs, and the response’s warnings[] carries { code: "FIELD_TYPE_MISMATCH", field, expectedType, actualType }.
  • Use a stable Idempotency-Key when firing from a webhook. See Idempotency for the contract and API design notes for the duplicate-delivery rationale.
  • The fire endpoint accepts and returns 202, with automationRunIds[] at the top level (no details wrapper), notStarted[] naming any matched automation that could not start and why, and the triggerInstanceId for the fire itself. A replay of the same Idempotency-Key answers 200 with status: "replayed" and the original run ids. Failures use the standard error envelope, so a bad payload is 400 INVALID_PAYLOAD. Don’t block on delivery. Poll the run with Async jobs & polling.
  • At least one bound automation must be Published. Triggers are always-on, but emails only fire when at least one bound automation has published: true. If every bound automation is in Draft, the fire call returns 422 NO_PUBLISHED_AUTOMATION. Publish the automation in the builder (or via brew.automations.patch({ published: true })) and retry.

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/automations/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: Each provider’s adapter normalizes 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

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

None of these paths exist today. /v1/webhook-subscriptions is not in the OpenAPI specification and is not served: every method on it answers 404 NOT_FOUND. The sketch below is a design intent, not a contract, and the names and shapes can still change before launch. Do not write code against it.

Planned Envelope

What to Do Today

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 explore it further with ChatGPT or Claude.