- Home
- Destinations
- Webhooks
Webhooks for your Postgres database.
Every committed insert, update and delete POSTed to an endpoint you own — signed, batched, retried, and in order per row. No triggers, no polling, no queue to run.
The webhook sink is the simplest way to react to database changes from any language: Waltail reads your database's write-ahead log over logical replication and POSTs each batch of changes as JSON to a URL. Your handler verifies the signature, processes the messages, and returns a 2xx.
Because the payload is plain JSON over HTTPS, the same sink reaches serverless functions, internal services, Slack and Discord incoming webhooks, workflow tools, and anything else that accepts a POST.
How delivery works
| Aspect | Behaviour for Webhooks |
|---|---|
| Request | One POST per batch, Content-Type: application/json, body {"messages": […]} with each change as an element in commit order. |
| Headers | X-Pgcdc-Event: changes; with a secret, X-Pgcdc-Signature = hex HMAC-SHA256 of the exact body. |
| Batching | Up to the sink's batch size per request (default 100). Set it to 1 for one request per change. |
| Ordering | Changes to the same row never overlap; array order inside a batch is commit order. |
| Idempotency | Each message carries a unique idempotency_key; store it to make your handler safe under redelivery. |
| On failure | Any 2xx is accepted. A 429 pauses the sink for the Retry-After period (30 s if absent) without spending a retry or metering egress. A failed request is retried up to 5 times with exponential backoff (30 s, 1, 2, 4 min, with jitter). After the fifth failure the message is parked as undelivered, the console shows it with the last error, and one click replays it — later changes to the same row wait behind it so order is preserved. |
| Connection check | Before the sink is saved, Waltail POSTs {"type":"pgcdc.check"} with X-Pgcdc-Event: check and expects a 2xx within 10 s. |
Set up in three steps
-
Connect your database
Paste a connection string and press Test. Waltail checks the version (12+), that
wal_levelislogical, that the user may replicate, and that a slot is free — and shows the fix for anything that fails. Then it creates its own publication and replication slot. Connection guide → -
Add Webhooks as a destination
Pick the tables to stream, choose Webhooks, and fill in Webhook URL. Press Test — Waltail checks it can reach and write to Webhooks before anything is saved.
-
Change a row
Insert or update a row. The console shows the first event as it is captured and delivered; from then on, every committed change follows within about a second. Have rows that already exist? Run a backfill — confirm the estimate and they arrive through Webhooks as
readevents.
Configuration
Fields as the console asks for them. Secrets are encrypted at rest and never shown again.
| Field | Console label | Required | Notes |
|---|---|---|---|
url | Webhook URL | Yes | Must start with http:// or https://. |
secret | Signing secret | Optional | When set, every request carries an HMAC-SHA256 signature of the body. |
Every sink also has two tuning settings: batch size (messages per request, default 100) and rate limit (messages per second, default unlimited). Changes to one row are never in flight twice at once.
Example
What Webhooks receives:
POST /hooks/waltail HTTP/1.1
Content-Type: application/json
X-Pgcdc-Event: changes
X-Pgcdc-Signature: 3f9a…c2e1
{"messages":[{ "op": "update", "table": "public.orders", "data": { "id": 42, "status": "shipped", "total": 19.99 }, "old": { "id": 42, "status": "paid", "total": 19.99 }, "seq": 137, "idempotency_key": "9f1c3d2e-6b4a-4c7e-9d0f-2a1b3c4d5e6f", "commit_lsn": "0/1A2B3C8" }]}
Questions
How do I verify the webhook signature?
Compute HMAC-SHA256 over the raw request body with your signing secret and compare it, constant-time, to the hex value in X-Pgcdc-Signature. Compute it over the bytes you received, before any JSON parsing.
What happens if my endpoint is down?
Waltail keeps the changes and retries with backoff for about 15 minutes, then parks them as undelivered. Nothing is lost on your database side — the change was acknowledged to Postgres only after it was stored. When the endpoint is back, replay everything undelivered in one click.
Can I receive one change per request?
Yes. Set the sink's batch size to 1. Larger batches are cheaper and faster for most consumers.
Can I send to Slack, Discord or a workflow tool?
Any service that accepts a JSON POST works. For services that expect their own body shape, put a small function or worker in between, or use a queue sink and shape the message there.