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

# Supabase

> Trigger Brew automations from Supabase auth events including signups, email confirmation, password changes, and bans, with user data synced as they fire.

Connect [Supabase](https://supabase.com) so events on the `auth.users` table fire Brew automations. Send a real welcome email after the email is verified, alert the user the moment their password changes, drip pending-invite reminders, or surface account-suspension notices.

## What You Can Do

* **Trigger automations from Supabase auth events.** Real signups, email + phone confirmations, password changes, bans, invites
* **Auto-sync contacts into your audience.** Every Supabase event upserts the user into Brew with `supabase_*` custom fields (`supabase_user_id`, `supabase_account_status`, `supabase_email_verified_at`, `supabase_phone_verified_at`, `supabase_last_sign_in_at`, etc.) you can filter and segment on
* **Get more than INSERT/UPDATE/DELETE.** Brew classifies column-level transitions into 12 marketing-relevant events (e.g. `auth.user.email_confirmed` fires the moment `email_confirmed_at` flips from null to set)
* **Stay safe at scale.** Brew signs every webhook with a shared secret you control; pgcrypto + Vault handle the HMAC inside Postgres

<Warning>
  Every marketing recipient must have opted in to hear from you. Importing or syncing contact data into Brew is not permission to email it. See [What You Can Send](/get-started/what-you-can-send).
</Warning>

## How It Works

1. Brew mints a unique webhook URL for your brand and generates the setup SQL you paste into your Supabase project.
2. The SQL installs the `pgcrypto` extension, stores your shared secret in Supabase Vault, and creates a trigger on `auth.users` that HMAC-signs every change before pg\_net POSTs it to Brew.
3. On receipt Brew verifies the signature against your shared secret. Any UPDATE is classified, if a specific column transition matches one of the 9 synthetic events (e.g. `email_confirmed_at` going from null to set), Brew fires that more-specific event instead of the generic `auth.user.updated`.
4. For each verified event Brew (a) starts every **Live** automation whose trigger matches and (b) upserts the Supabase user into your [audience](/audience/add-contacts) with `supabase_*` custom fields.

See [How event triggers work](/integrations/integrations#how-event-triggers-work) for the conceptual flow shared by every event-source integration.

## Setup

<Steps>
  <Step title="Generate your Brew webhook URL">
    In Brew, open **Integrations**, click the **Supabase** card, and click **Generate URL**. The SQL block in step 3 will pre-fill the URL for you.
  </Step>

  <Step title="Generate a 32-character shared secret">
    Run `openssl rand -hex 32` in your terminal. You'll paste this same value into the SQL block (step 3) and the Brew connect form (step 4).
  </Step>

  <Step title="Run the setup SQL in Supabase">
    In your Supabase project, open **SQL Editor → New query**. Paste the SQL block that Brew shows in the connect dialog, replace `<PASTE_YOUR_SECRET_HERE>` with the secret from step 2, then click **Run**.

    The block installs the pgcrypto extension, stores your shared secret in Supabase Vault, and creates a trigger on `auth.users` that signs every change with HMAC-SHA-256 before pg\_net POSTs it to Brew.
  </Step>

  <Step title="Paste the same secret into Brew">
    Paste the secret from step 2 into the Brew connect form and click **Connect**. Brew uses it to verify every webhook the new trigger sends.
  </Step>

  <Step title="Verify it's working">
    Sign up a test user in your Supabase Auth UI (or run an `INSERT` into `auth.users` via the SQL Editor). Within a few seconds it should appear in the **Manage → Recent events** panel of the Supabase integration page in Brew. If it doesn't, recheck the Vault entry and that the trigger SQL ran cleanly.
  </Step>
</Steps>

<Note>
  Re-running the SQL block after rotating the secret is safe. The block drops and re-creates the trigger idempotently.
</Note>

## Triggering Automations

Once connected, every Supabase event below is provisioned automatically and available as a trigger in the [automation builder](/create-emails/automations). The **Manage** tab is a read-only catalogue plus a live **Recent events** stream, no per-event enable step. Whether an event fires is controlled by whether the bound automation is **Live**.

### Supported Events

Brew supports 12 Supabase auth events, 3 raw envelope types and 9 synthetic UPDATE classifications:

**Raw envelope events**

* `auth.user.created`: INSERT into `auth.users`
* `auth.user.updated`: UPDATE that didn't match a synthetic classification
* `auth.user.deleted`: DELETE from `auth.users`

**Synthetic UPDATE events**

* `auth.user.email_confirmed`: `email_confirmed_at` transitions from null to set
* `auth.user.phone_confirmed`: `phone_confirmed_at` transitions from null to set
* `auth.user.email_changed`: `email` column changes between rows
* `auth.user.password_changed`: `encrypted_password` changes between rows
* `auth.user.signed_in`: `last_sign_in_at` changes
* `auth.user.banned`: `banned_until` transitions from null to set
* `auth.user.unbanned`: `banned_until` transitions from set to null
* `auth.user.invited`: `invited_at` transitions from null to set
* `auth.user.upgraded_from_anonymous`: `is_anonymous` flips from true to false

<Note>
  A single UPDATE only fires one synthetic event, the highest-priority classification wins. Email confirmation beats phone confirmation, which beats anonymous-to-real upgrade, and so on.
</Note>

<Tip>
  In chat, say *"Send the real welcome email after the user confirms their email"* and Brew will scaffold the automation with `auth.user.email_confirmed` pre-selected.
</Tip>

## Common Patterns

| Goal                                        | Supabase event                      |
| ------------------------------------------- | ----------------------------------- |
| Initial signup notification                 | `auth.user.created`                 |
| Real welcome email (after verification)     | `auth.user.email_confirmed`         |
| Security alert on a password change         | `auth.user.password_changed`        |
| Security alert sent to the **old** email    | `auth.user.email_changed`           |
| Pending-invite reminder drip                | `auth.user.invited`                 |
| Welcome anonymous users after they convert  | `auth.user.upgraded_from_anonymous` |
| Account-suspension notice                   | `auth.user.banned`                  |
| Welcome-back after a successful appeal      | `auth.user.unbanned`                |
| Re-engagement detection for returning users | `auth.user.signed_in`               |

<Warning>
  `auth.user.signed_in` also fires on Supabase token refreshes. Add a per-user filter or a `wait` node if you want a true "they came back after a long time" signal.
</Warning>

## Personalization

These resolve as [merge tags](/create-emails/merge-tags) in the email body, subject line, and preview text. Use a fallback wherever an empty value would read badly, like `{{ firstName | there }}`.

| Variable                               | Example                     |
| -------------------------------------- | --------------------------- |
| `{{ email }}`                          | `alex@example.com`          |
| `{{ firstName }}`                      | `Alex`                      |
| `{{ phone }}`                          | `+15555550100`              |
| `{{ provider }}` (signup method)       | `email`, `google`, `github` |
| `{{ previousValue }}` (e.g. old email) | `old@example.com`           |
| `{{ newValue }}` (e.g. new email)      | `alex@example.com`          |

<Note>
  The Supabase user UUID is **not** available as a merge tag: it never reaches the trigger payload. Brew writes it to the synced contact as the `supabase_user_id` custom field instead, so filter, segment, and join on it there.
</Note>

## Troubleshooting

<AccordionGroup>
  <Accordion title="An event arrived but no automation fired" icon="circle-question">
    Open the canvas and confirm at least one automation bound to this event is set to **Live** (not Draft). The Brew integration's **Manage → Recent events** panel confirms the webhook is arriving, if it isn't there, the issue is upstream (pgcrypto trigger not installed, secret mismatch). If the event is there but no email sent, the only remaining gate is `automation.published`.
  </Accordion>

  <Accordion title="Webhooks aren't arriving at all" icon="triangle-exclamation">
    Re-run the setup SQL block from the integration page. The block is idempotent, it'll re-create the trigger and re-write the Vault entry. Confirm that `pgcrypto` is installed (`SELECT * FROM pg_extension WHERE extname = 'pgcrypto';`) and that the secret in Vault matches the one pasted into Brew.
  </Accordion>

  <Accordion title="auth.user.signed_in fires constantly" icon="rotate">
    Supabase token refreshes also bump `last_sign_in_at`, which fires `auth.user.signed_in` alongside true sign-ins. Add a per-user `wait` or `filter` node in your automation to ignore repeats within a short window, or rely on `auth.user.email_confirmed` / `auth.user.upgraded_from_anonymous` for higher-signal events.
  </Accordion>

  <Accordion title="I want to test before going live" icon="flask">
    Insert a test row into `auth.users` (or sign up via your normal flow) and watch the **Manage → Recent events** panel. Build your automation as a Draft, run a test send from the email editor, then publish it to **Live** once you're happy.
  </Accordion>
</AccordionGroup>

## Requirements

* A Supabase project where you can run SQL against the `auth` schema
* Permission to install extensions (`pgcrypto`) and write to Supabase Vault

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