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

# Automation Triggers

> Reference for what starts a Brew automation: custom events and their payload contracts, connected-source events, unsubscribe events, and manual audiences.

Every automation starts with a trigger. This page is the reference for each kind of trigger, what a fire saves on the contact, and how Brew checks a payload.

For the nodes that follow the trigger, see [Automations](https://docs.brew.new/create-emails/automations.md). To build a flow on a trigger, follow [Build an Automation](https://docs.brew.new/create-emails/build-an-automation.md).

## Trigger Events

The trigger picker offers four kinds of trigger:

* **Custom HTTP triggers.** Events you define and fire from your backend.
* **Built-in events.** Discovered from connected sources such as Stripe billing events.
* **Unsubscribe events.** Brew starts these itself when a contact unsubscribes.
* **Manual audiences.** A saved audience, run through the flow on demand.

Common trigger moments include new user signup, order placed, trial ending, payment failed, cart abandoned, and subscription canceled.

Manage the events on the **Triggers** page: open **Automations** and click **Triggers**.

## Changing the Trigger

Click the trigger node to open its config panel. Use **Change trigger** to select another audience or event without rebuilding the rest of the flow.

## Custom Events

A custom event is one you define and fire from your own backend. It has a name and a payload schema, and Brew checks every fire against it before starting any automation. A missing required field or a wrong type fails the fire.

Undeclared keys depend on the enforcement mode of the trigger's stored contract:

* **Off**, the default, accepts them and reports a warning.
* **Prune** drops them before the run.
* **Strict** refuses the fire.

A field with a fallback value never fails a fire for being missing. Every recipient gets the fallback. See [Typed Payload Contracts](https://docs.brew.new/api-reference/guides/typed-payload-contracts.md).

**Names** describe the action, such as `user_signup`, `order_placed`, or `subscription_renewed`.

**Schemas** list the fields the event sends, each typed as `string`, `int`, `boolean`, `object`, or `array`, with required fields marked. A stored contract adds `float`, `date`, and `enum`. A flat payload, like the one in [Build an Automation](https://docs.brew.new/create-emails/build-an-automation.md#create-a-custom-trigger-event), keeps merge tags short.

A field can also be an object. Declare the nesting and each leaf is addressable by its dotted path, so this schema gives you `{{ user.email }}` and `{{ order.id }}`:

```json theme={null}
{
  "user": { "email": "alex@example.com", "name": "Alex" },
  "order": { "id": "ORD-12345" }
}
```

Either shape works. Undeclared keys never reach the template, and an automation whose email uses a trigger field the trigger doesn't declare can't publish.

**What a fire saves on the contact.** Each fire also creates or updates the contact in its `email` field:

* **Top-level fields are saved.** `firstName`, `lastName`, and `subscribed` fill the contact's own fields. Other declared top-level fields become custom properties.
* **Objects and lists aren't saved.** A field like `order` stays available to the email as `{{ order.id }}`, but it's not stored on the contact.
* **Fallbacks aren't saved.** A fallback value fills the email only. A `subscribed` fallback only sets a new contact's subscription.
* **A value of the wrong type is skipped.** Sending `"abc"` for a number field leaves that field unchanged. The rest of the contact is saved, the automation still runs, and the response carries a `FIELD_TYPE_MISMATCH` warning.

Create custom events on the **Triggers** page with **New custom trigger**, and fire them from your backend. [Build an Automation](https://docs.brew.new/create-emails/build-an-automation.md#create-a-custom-trigger-event) walks through creating, testing, and firing one, with advice on naming and schemas. The [API Reference](https://docs.brew.new/api-reference/api/api-introduction.md) has the contract.

### What Happens When a Payload Fails Validation

* **Response.** A missing required field or a wrong type returns `400 INVALID_PAYLOAD`, and no automation starts.
* **Field errors.** `details.errors[]` names every field that failed, each with a `field` and a `code`: `missing_required`, `invalid_type`, `unexpected_key`, `invalid_email`, or `payload_not_object`.
* **Retries.** The same body fails the same way on retry. Fix the payload against the trigger's `payloadSchema`, then send the event again.

[Errors](https://docs.brew.new/api-reference/api/errors.md) owns the envelope and the full code catalog.

## Connected-Source Events

Connecting a provider provisions every event Brew supports for it. Most connections mint a webhook URL scoped to your brand, which you paste into the provider's dashboard. Stripe registers its own. Brew signature-verifies every delivery and rejects tampered or replayed events before anything runs. For every verified event, Brew starts each **Live** automation whose trigger matches and upserts the contact into your audience with provider-namespaced custom fields (`stripeSubscriptionStatus`, `clerkUserId`, and so on) you can use in filters and merge tags. The integration's **Manage** tab shows the catalogue and a **Recent events** stream.

Until the provider sends an event at least once, Brew can't tell whether its webhook is set up. You can still publish an automation on it, with a `TRIGGER_EVENT_NOT_RECEIVED` warning, and it runs once the event arrives. To check, `check_trigger` over MCP or `GET /v1/automations/triggers/{triggerEventId}/readiness` returns `delivery`: `received` once Brew has seen the event, or `never_received` with the setup step to check.

An event fires emails only when a **Live** automation is wired to it. Unpublish the automation to stop new people from entering; anyone already in the flow still finishes. Pause it to hold every send, or disconnect the integration to stop every event.

| Source | What it covers | Event count |
| - | - | - |
| [Clerk](https://docs.brew.new/integrations/import/clerk.md) | User signups, organizations, subscriptions, waitlist entries | 13 |
| [Stripe](https://docs.brew.new/integrations/import/stripe.md) | Checkouts, customers, subscriptions, invoices, quotes, payments, refunds | 22 |
| [Stytch](https://docs.brew.new/integrations/import/stytch.md) | Consumer and B2B auth, members, organizations | 7 |
| [Supabase](https://docs.brew.new/integrations/import/supabase.md) | `auth.users` and derived auth events | 12 |
| [WorkOS](https://docs.brew.new/integrations/import/workos.md) | SSO users, password reset, organizations, SCIM | 18 |
| [Shopify](https://docs.brew.new/integrations/import/shopify.md) | Customers, orders, checkouts, fulfillment | 13 |
| [RevenueCat](https://docs.brew.new/integrations/import/revenuecat.md) | In-app purchases and subscriptions | 11 |
| [Framer](https://docs.brew.new/integrations/import/framer.md) | Form submissions | 1 per form |

See the [Integrations overview](https://docs.brew.new/integrations/integrations.md) for the full list. For the flows a store usually runs on these events, see [Lifecycle Emails for E-Commerce](https://docs.brew.new/recipes/lifecycle-emails-ecommerce.md). Brew only listens to connected sources. It does not charge customers, modify subscriptions, refund payments, mutate auth records, or change Shopify orders.

## Unsubscribe Events

An automation can start when a contact unsubscribes, either through an email's unsubscribe link or their mail app's one-click button. Bounces, complaints, imports, and edits through the API never start one. The trigger picker lists it as **When a contact unsubscribes**, and **Which unsubscribes start it?** sets the list it watches:

* **All emails.** The contact unsubscribed from everything your brand sends.
* **One marketing domain.** Each marketing domain is listed by name. The contact left that domain's list. An unsubscribe from all, clicked in one of that domain's emails, starts it too, as long as the contact was still on the list.

Only marketing domains appear. A transactional domain keeps no unsubscribe list, so there is nothing to leave.

Brew owns these triggers. A domain's trigger is named after it, such as **Unsubscribed from news.acme.com**. The title and payload are fixed, there is nothing to set up, and they can't be fired over HTTP.

The payload is `{ email, unsubscribe: { scope, domain?, method, reasons, reasonText?, emailId?, emailTitle?, sendId?, at } }`. Emails reference it through merge tags such as `{{ unsubscribe.domain }}` and `{{ unsubscribe.reasonText }}`.

A field marked `?` can be absent, and its merge tag then renders blank. `domain` is missing when the link came from a transactional email. `reasons` is always there, empty when none was chosen, as after a one-click.

A test of the automation runs on Brew's sample unsubscribe, and any payload fields you pass replace the sample's.

Marketing email from the list the contact left never reaches them. Mail from another marketing domain reaches them only while they are still on that domain's list. After an unsubscribe from all, no marketing domain does. Brew refuses to publish a flow none of whose emails could reach them.

From an agent, `ensure_brew_event_trigger` returns the brand's trigger for a scope. The API, SDK, and CLI calls are in [From an Agent or Code](#from-an-agent-or-code). To set up a flow on this trigger, see [Start a Flow When Someone Unsubscribes](https://docs.brew.new/create-emails/build-an-automation.md#start-a-flow-when-someone-unsubscribes).

## Manual Audience

A manual-audience automation runs a saved [audience](https://docs.brew.new/audience/create-audiences.md) through the graph on demand. Its trigger shows the audience, filter query, and contact count. Runs show **Sending** while in flight and **Sent** when complete.

Manual-audience automations are one-off flows. Choose **Run now**, **Schedule**, or **Gradual send** when launching one. See [Send Options](https://docs.brew.new/create-emails/send-options.md#when-you-launch-an-automation) for details.

The API and MCP also support manual-audience runs, including dry-runs. While a run is in flight, an agent or your code can control it:

* **Pause** stops it at the next step. Emails already going out may finish.
* **Resume** continues delivery. It also retries a failed run from its first undelivered step, skipping steps that already completed.
* **Cancel** stops the remaining steps for good. Emails already delivered stay sent.

A run held at your plan's send limit resumes on its own. Over MCP these are `pause_audience_run`, `resume_audience_run`, and `cancel_audience_run`.

## From an Agent or Code

* **Fire a custom event.** `POST /v1/automations/triggers/{triggerEventId}/fire`. [Build an Automation](https://docs.brew.new/create-emails/build-an-automation.md#from-an-agent-or-code) has the request and the error codes.
* **Get the brand's trigger for a scope.** `POST /v1/automations/triggers/brew-events` from code, `brew.automations.triggers.ensureBrewEvent()` in the SDK, and `brew-cli automations triggers ensure-brew-event` in the CLI.
* **Read a trigger's contract.** `GET /v1/automations/triggers/{triggerEventId}` returns its `payloadSchema`, the shape the [validation error](#what-happens-when-a-payload-fails-validation) checks against.

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

> [tab] Self-Service Tools
>
> > [card] Search Documentation
> >
> > Type in the "Ask any question" search bar at the top left to instantly find relevant documentation pages.
>
> > [card] [Ask ChatGPT or Claude](#top)
> >
> > Click here to open the **Copy page** menu at the top of this page, then send the page to ChatGPT, Claude, or your editor.

> [tab] Talk to Our Team
>
> > [card] [Schedule a Call](https://cal.com/team/brewdotnew/brew-demo)
> >
> > Book time with our founders for personalized guidance on strategy, best practices, or complex implementation questions.
>
> > [card] Call Us Directly
> >
> > Need immediate assistance? Reach us at **+1-(332)-203-2145** for urgent issues or time-sensitive questions.
>
> > [card] Slack Channel
> >
> > Our preferred support channel. You'll receive an invite after signup for direct founder support and fast responses.
>
> > [card] [Email Support](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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.