> ## 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.

# Events & Webhooks

> Event-driven integration with the Brew Public API v1: you-call-us today, we-call-you on the roadmap.

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)

| Direction                                                                                                                                                                                     | Who calls who                                                                     | Status     |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------- |
| **You → Brew**: fire a custom event from your backend so Brew runs the matching automation                                                                                                    | `POST /v1/automations/triggers/{triggerEventId}/fire { 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 once | Per-integration setup in the [Integrations tab](/integrations/integrations)       | ✅ Shipped  |
| **Brew → You**: outbound webhooks for run / send / email-event lifecycle (`run.completed`, `send.delivered`, `send.opened`, …)                                                                | Coming: see [roadmap](#outbound-webhooks-roadmap)                                 | 🚧 Planned |
| **Brew → You**: SSE / WebSocket streaming for live run logs                                                                                                                                   | Coming with outbound webhooks                                                     | 🚧 Planned |

Until outbound webhooks ship, the supported "wait for completion" pattern is polling. See [Async jobs & polling](/api-reference/api/async-jobs).

## Inbound: You Call Brew

### Pattern: Fire a Custom Event

The canonical agent / backend integration path:

```
your-backend → POST  /v1/automations/triggers              → mint triggerEventId
your-backend → POST  /v1/emails                            → mint email bodies (untyped designs)
your-backend → GET   /v1/domains                           → pick verified sending domain
your-backend → POST  /v1/automations                       → assemble graph (trigger → sendEmail → wait → …)
your-backend → PATCH /v1/automations/{automationId}        → { published: true } to go live
your-backend → POST  /v1/automations/triggers/{id}/fire    → fire whenever the real-world event happens
```

The fire call:

```bash theme={null}
curl -X POST https://brew.new/api/v1/automations/triggers/tri_signup/fire \
  -H "Authorization: Bearer $BREW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: signup-jane@example.com-1779292800" \
  -d '{
    "payload": { "email": "jane@example.com", "firstName": "Jane" }
  }'
# → 200 { 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 endpoint 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 double-fired webhook = doubled emails. See [Idempotency](/api-reference/api/idempotency).
* **The fire endpoint returns the legacy fire envelope** (`200`) with `details.automationRunIds[]`. It's the one v1 endpoint that wraps both success and failure in this shape. Don't block on delivery. Poll the run with [Async jobs & polling](/api-reference/api/async-jobs).
* **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:

| Provider   | Common events                                                                       |
| ---------- | ----------------------------------------------------------------------------------- |
| Clerk      | `user.created`, `user.updated`, `organization.created`                              |
| Stripe     | `customer.subscription.created/updated/deleted`, `invoice.paid`, `customer.deleted` |
| Shopify    | `orders/paid`, `customers/create`, `customers/update`                               |
| Stytch     | `user.created`, `user.deleted`, `session.created`                                   |
| Supabase   | `INSERT`, `UPDATE`, `DELETE` on configured tables                                   |
| WorkOS     | `dsync.user.created`, `dsync.user.updated`, `dsync.user.deleted`                    |
| RevenueCat | `INITIAL_PURCHASE`, `RENEWAL`, `CANCELLATION`, `EXPIRATION`                         |

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](/changelog/overview) for the launch announcement.

### Planned Event Types

| Event                                                        | Fires when                                                       |
| ------------------------------------------------------------ | ---------------------------------------------------------------- |
| `email.sent`                                                 | An email is handed to the upstream ESP.                          |
| `email.delivered`                                            | The recipient's MX accepted the message.                         |
| `email.opened`                                               | The recipient opened the message (one event per open).           |
| `email.clicked`                                              | The recipient clicked a tracked link.                            |
| `email.bounced`                                              | Hard or soft bounce.                                             |
| `email.complained`                                           | The recipient marked the message as spam.                        |
| `email.unsubscribed`                                         | The recipient unsubscribed via the footer link.                  |
| `send.queued`                                                | A `POST /v1/sends` campaign was accepted.                        |
| `send.scheduled`                                             | A scheduled send was queued for future delivery.                 |
| `send.completed`                                             | All recipients of a send job were handled.                       |
| `send.failed`                                                | A send job failed to start (e.g. domain not ready).              |
| `run.started`                                                | An automation run started a workflow.                            |
| `run.completed`                                              | A run finished all nodes successfully.                           |
| `run.failed`                                                 | A run errored out at a node.                                     |
| `run.cancelled`                                              | A run was cancelled (P7: when cancel ships).                     |
| `contact.created` / `contact.updated` / `contact.suppressed` | Contact lifecycle events.                                        |
| `trigger.fired`                                              | A 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

```http theme={null}
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

```json theme={null}
{
  "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

| Need                                                                    | Today's pattern                                                                                                                       |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| React to inbound events (signup, purchase, churn) and run an automation | `POST /v1/automations/triggers/{triggerEventId}/fire`, works now                                                                      |
| Wait for a fire to complete                                             | Poll `GET /v1/automations/runs?automationRunId=…`. See [Async jobs](/api-reference/api/async-jobs)                                    |
| Receive provider webhooks (Stripe, Clerk, Shopify, …) into Brew         | Configure the integration in the dashboard; Brew hosts the receive URL                                                                |
| Notify your backend when an email opens / delivers                      | Roadmap: 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 UI                                          | Roadmap: SSE. Today: poll `GET /v1/automations/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

* [Authentication](/api-reference/api/authentication): how `Authorization: Bearer` works on the inbound side.
* [Async jobs & polling](/api-reference/api/async-jobs): the supported "wait for completion" pattern today.
* [Idempotency](/api-reference/api/idempotency): pair with every inbound retry.
* [Integrations](/integrations/integrations): set up provider receivers (Clerk, Stripe, Shopify, …).

## 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:

<Tabs>
  <Tab title="Self-Service Tools">
    <CardGroup cols="2">
      <Card title="Search Documentation" icon="magnifying-glass" color="#c44925">
        Type in the "Ask any question" search bar at the top left to instantly find relevant documentation pages.
      </Card>

      <Card title="ChatGPT/Claude Integration" icon="robot" color="#c44925">
        Click "Open in ChatGPT" at the top right of any page to analyze documentation with ChatGPT or Claude for deeper insights.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="Talk to Our Team">
    <CardGroup cols="2">
      <Card title="Schedule a Call" icon="calendar" color="#c44925" href="https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1iYoRUG1J792XQpbuQLjSRRDupr7MwraFK-HQRCtTYdBmrQi8nZu2qXfzKQigb8gbKJK3KN3-R">
        Book time with our founders for personalized guidance on strategy, best practices, or complex implementation questions.
      </Card>

      <Card title="Call Us Directly" icon="phone" color="#c44925">
        Need immediate assistance? Reach us at **+1-(332)-203-2145** for urgent issues or time-sensitive questions.
      </Card>

      <Card title="Slack Channel" icon="slack" color="#c44925">
        Our preferred support channel. You'll receive an invite after signup for direct founder support and fast responses.
      </Card>

      <Card title="Email Support" icon="envelope" color="#c44925" href="mailto:support@brew.new">
        Contact us at **[support@brew.new](mailto:support@brew.new)** for detailed inquiries or if you prefer not to use Slack.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
