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 needUse
Gate on state that lives in your Postgres tablesThis recipe — server-authoritative, tamper-proof
Referral links, deferred deep linking, invite/status UIThe Invitebase SDK — attribution still needs the client
Purchase/subscription gatesRevenueCat, Adapty, or Stripe Billing — richer subscription semantics
Your backend is Firebase, not SupabaseFirebase — 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:

supabase/functions/invitebase-forward/index.ts
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-jwt

Deploying 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 UPDATE webhooks, compare record to old_record and 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 value property (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_response table 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-Key never double-count toward a gate.

What's next

On this page