Hooks
Hooks run logic when a record changes — without you writing a custom endpoint. Send a welcome email when a user signs up, keep a running total when an order is paid, or reject a write that breaks a business rule. They're declared in hooks.yaml and attached to a table and an event.
After-hooks
on_insert, on_update, and on_delete fire after a successful mutation and run asynchronously, so they never slow the response. Each hook can run SQL, send an email, fire an outbound webhook, or drop an in-app notification.
on_insert:
contacts:
- email:
to: ""
subject: "Welcome"
template: "welcome"
- notify: ""
title: "Contact created"
on_update:
orders:
- sql: "UPDATE stats SET total = total + "
when: "status = 'paid' AND old_status != 'paid'"
Before-hooks
before_insert, before_update, and before_delete run synchronously and can abort the mutation. Use them to enforce invariants the database alone can't — the request fails with a 422 and the row is never written.
before_insert:
orders:
- sql: "SELECT CASE WHEN amount > 10000
THEN RAISE(ABORT, 'amount over limit') END"
What's available inside a hook
- The new row's columns by name:
,,. - Session context:
,,,. - On updates, the previous values as
— so awhen:condition can fire only on a real change (e.g.status = 'paid' AND old_status != 'paid').
Hooks vs. flows
Reach for a hook when the trigger is an ordinary CRUD change and the work is a side effect. Reach for a flow when you need a custom URL, a request/response shape of your own, or multi-step orchestration. They compose: a flow can write a row whose on_insert hook then sends the notification.