Supabase
Turn Supabase database changes into qualification gate events — server-authoritative, no client instrumentation
If your app's source of truth is Supabase, the moments that should validate a referral — an order row inserted, a profile marked complete, a subscription flipped to active — already happen in Postgres. Forward those row changes to Invitebase and they become qualification gate conditions, with no client-side tracking and no way for a tampered client to fake progress.
Unlike RevenueCat, Adapty, and Stripe Billing, this is not a Connect a source connector — it's a small Edge Function you own that maps a database change to POST /v1/events. About twenty lines, shown below in full.
When to use this vs the SDK
| You need | Use |
|---|---|
| Gate on state that lives in your Postgres tables | This recipe — server-authoritative, tamper-proof |
| Referral links, deferred deep linking, invite/status UI | The Invitebase SDK — attribution still needs the client |
| Purchase/subscription gates | RevenueCat, Adapty, or Stripe Billing — richer subscription semantics |
| Your backend is Firebase, not Supabase | Firebase — the same recipe on Firestore |
Most apps run the SDK for attribution and sharing, and forward database changes for gates. Both event streams drive gate progress identically.
Forward a table change
The example gates on a completed order: an INSERT into public.orders becomes an order_completed event.
Create an Edge Function that maps the row change to an Invitebase event:
Deno.serve(async (req) => {
// Database Webhook payload: { type, table, record, old_record }
const change = await req.json();
if (change.table !== "orders" || change.type !== "INSERT") {
return new Response("ignored", { status: 200 });
}
const res = await fetch("https://api.invitebase.com/v1/events", {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("INVITEBASE_SECRET_KEY")}`,
"Content-Type": "application/json",
// one key per row change — retries never double-count
"Idempotency-Key": `orders-insert-${change.record.id}`,
},
body: JSON.stringify({
name: "order_completed",
referred_user_id: change.record.user_id,
properties: { value: change.record.total_cents },
}),
});
return new Response(null, { status: res.ok ? 200 : 500 });
});referred_user_id must be the same user ID you pass to the SDK's identify() — with Supabase Auth that's usually the auth.users UUID already sitting in your row. Events for users who aren't part of an open referral are accepted and count for nothing, so you don't need to filter to referred users yourself.
Set your secret key and deploy:
supabase secrets set INVITEBASE_SECRET_KEY=sk_test_...
supabase functions deploy invitebase-forward --no-verify-jwtDeploying with --no-verify-jwt lets the Database Webhook call the function without a user JWT. To lock it down, set a shared-secret header on the webhook (next step) and reject requests without it at the top of the function.
Create the Database Webhook: in the Supabase dashboard go to Database → Webhooks → Create a new hook, choose the orders table and the Insert event, and point it at your function URL (https://<project-ref>.supabase.co/functions/v1/invitebase-forward). Prefer SQL migrations? The equivalent CREATE TRIGGER … supabase_functions.http_request(...) works the same.
Insert a test row. The event appears in Developer tools → Events within seconds with source: api, and order_completed now populates the dropdowns in the gate builder — add a condition on it like any other event.
Patterns that work well
- Gate on transitions, not states. For
UPDATEwebhooks, comparerecordtoold_recordand forward only the transition you care about (old_record.status !== 'complete' && record.status === 'complete'), with the transition baked into the idempotency key. - Sum toward a target. Send a
valueproperty (minutes, cents, items) and the gate builder can sum it toward a target — see gate examples. - One function, many tables. Point several Database Webhooks at the same function and switch on
change.table— a small event map beats a function per table.
Environments
Postgres has no test/live concept, so bind each Supabase project to a mode: your staging project's function gets a test-mode secret key, production gets live. Test-mode events light up the same gate progress panels so you can rehearse the whole flow before launch.
Troubleshooting
- Events arrive but gates never progress — the forwarded ID doesn't match the referred user's identified ID. Inspect the event in the event log and compare it to the referral's participant.
- Nothing arriving — check the function's logs in the Supabase dashboard, and the
net._http_responsetable for the webhook's delivery results. The usual suspects: JWT verification still on, or a typo in the function URL. - Duplicates in the event log — expected if the webhook retried; deliveries with the same
Idempotency-Keynever double-count toward a gate.