Skip to content
Open beta — everything’s free right now, and your rate is locked when it ends.
← All guides

Send database changes to Make or n8n with a webhook

A webhook is your database telling something else that a record changed. The practical version: what a good payload contains, why signatures matter, how to survive a failed delivery, and how to make the whole thing idempotent.

Polling is the thing you do when nothing tells you what changed: a script wakes every five minutes, asks for everything, and compares. It works, it's wasteful, and it's always five minutes late.

A webhook inverts it. The database calls you — an HTTP POST to a URL you own, the moment a record changes.

The shape of it

  1. Your automation tool gives you a URL. In Make that's a Custom webhook module; in n8n it's the Webhook trigger node. Both hand you a URL and then sit and wait.
  2. You register that URL with your database, choosing which events fire it — created, updated, deleted — and usually which table.
  3. Something changes. The database POSTs JSON to your URL. Your scenario runs.

That's the whole mechanism. The rest is what separates one that works from one that quietly stops working in March.

What a good payload contains

The difference between a webhook you can build on and one that makes you call the API again for every event:

  • What happened, and to what — the event type, the table, the record id.
  • The record after the change. Obvious, and not universal.
  • The record before it. This is the one people miss until they need it. Without a before, you cannot tell "status became Confirmed" from "status was already Confirmed and the notes changed" — and those usually want different behaviour.
  • Which fields actually changed. Saves you diffing two objects to find out.
  • A delivery id, so you can recognise the same delivery arriving twice.

Tabla's deliveries carry all five: event, table, record_id, changed_field_ids, and before and after blocks. The exact shape is in the API reference.

Verify the signature. Actually verify it.

A webhook URL is a public URL. Anyone who learns it can POST to it, and your scenario has no way to tell that apart from a genuine delivery unless you check.

The standard fix: the sender signs the body with a shared secret and puts the signature in a header; you recompute it and compare.

const crypto = require("crypto");

function verify(rawBody, header, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)          // the RAW bytes — not a re-serialised object
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}

Two details that bite:

  • Sign the raw body. If your framework parsed the JSON and you re-stringify it to check the signature, key order or whitespace can differ and every delivery will look forged.
  • Compare in constant time. === on a signature leaks how much of it you got right, one character at a time. Use timingSafeEqual.

Assume it will be delivered twice

Networks retry. A delivery that timed out on your side may have succeeded, and the sender doesn't know that, so it sends again. Any webhook consumer that hasn't planned for this will eventually create two invoices for one booking.

The fix is to make the handler idempotent rather than to hope the sender is exactly-once:

  • Keep the delivery_id of everything you've processed and drop repeats.
  • Or make the action naturally repeatable — "set status to Confirmed" is safe to run twice; "add one to the counter" is not.

Respond fast, work later

Your endpoint should acknowledge and get out. Do the actual work afterwards.

A sender waiting on your scenario will eventually treat a slow response as a failure and retry it — so a scenario that takes forty seconds doing real work can end up running three times for one change. Return 200 immediately, queue the work.

When it fails anyway

It will. The receiving service has an outage, someone rotates a URL, a scenario gets disabled. What matters is what happens next:

  • Is there a delivery log? You need to see that it failed at all.
  • Can you replay? Fixing the endpoint is only half the job if the events that fired during the outage are gone.
  • What happens after repeated failures? Most senders eventually pause a webhook that keeps failing, which is correct — and worth knowing about, so a paused webhook doesn't look like "no changes happened".

Tabla keeps a delivery log, retries on a published schedule, lets you replay any delivery from the log, and auto-pauses an endpoint that keeps refusing — all of which is documented in the API reference.

The check before you ship

Send yourself a real change and confirm, in order: the delivery arrived, the signature verified, the before block is what you expected, and running the same delivery twice does not produce two of anything.

Tabla is the database we build these on.

A no-code database with real Postgres underneath: every feature on every plan, no record ceiling, and your whole database back out in one file, any day.

More guides