Prompt
How do I send an outbound webhook immediately when a database row changes?
Latest observation
To send an outbound webhook immediately when a database row changes, the usual pattern is:
Best practice: trigger change event, then send webhook asynchronously
- Row changes in the DB
- Database emits a change event
- A worker/service receives the event
- That worker calls the webhook
This avoids blocking the transaction and is much more reliable than trying to call the webhook directly from the database.
Common implementation options
1) Application-level hook
If your app updates the row, have the application code send the webhook after the write succeeds.
Pros
- Simple
- Full control over payload/auth/retries
Cons
- Misses changes made outside the app
- Can become brittle if multiple services write to the DB
Good for: small systems, one writer, low complexity
2) Database trigger + outbox table
Use a DB trigger to insert a record into an outbox table whenever the row changes. Then a background worker polls or streams that outbox table and sends the webhook.
Why this is good
- The DB transaction and event record are committed together
- Prevents losing events
- Lets you retry safely if the webhook fails
Typical flow
UPDATE orders SET status='shipped' ...- trigger writes row into
webhook_outbox - worker reads unsent outbox rows
- worker POSTs webhook
- worker marks event as sent
This is the most common reliable pattern.
3) CDC (Change Data Capture)
Use a database log stream such as:
- Postgres logical replication / WAL
- MySQL binlog
- Debezium
- AWS DMS
- Kafka Connect
Then a consumer transforms change events into webhook calls.
Pros
- Very scalable
- Captures all changes
- Works across many writers
Cons
- More infrastructure
- More complex setup
Good for: larger systems, event-driven architecture
What not to do
Don’t send the webhook directly inside the DB trigger
Because:
- triggers run inside the transaction
- webhook failures can slow down or fail the DB write
- external network calls from triggers are hard to retry safely
- database may not support HTTP calls natively without extensions, and those are often discouraged
Recommended design: outbox pattern
This is usually the safest “immediately after row change” solution.
Example schema
CREATE TABLE webhook_outbox (
id BIGSERIAL PRIMARY KEY,
event_type TEXT NOT NULL,
aggregate_type TEXT NOT NULL,
aggregate_id TEXT NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now(),
sent_at TIMESTAMP NULL,
attempts INT NOT NULL DEFAULT 0
);
Trigger example
CREATE OR REPLACE FUNCTION enqueue_webhook_event()
RETURNS trigger AS $$
BEGIN
INSERT INTO webhook_outbox(event_type, aggregate_type, aggregate_id, payload)
VALUES (
'row.updated',
TG_TABLE_NAME,
NEW.id::text,
to_jsonb(NEW)
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Background worker logic
- fetch unsent rows
- send HTTP POST to your webhook endpoint
- if success: set
sent_at - if failure: increment attempts and retry later
If you truly need “instant”
If “immediately” means sub-second, you can still use the outbox pattern with:
- a worker running continuously
- LISTEN/NOTIFY in PostgreSQL to wake the worker
- a queue like RabbitMQ, SQS, Kafka, or Redis Streams
A good hybrid is:
- trigger writes to outbox
- trigger also sends
NOTIFY - worker receives
NOTIFY, then reads the outbox row and sends the webhook
This gives low latency without sacrificing durability.
Important reliability features
No matter which approach you choose, include:
- retries with backoff
- idempotency keys
- dead-letter handling
- signing webhook requests (HMAC)
- timeout limits
- audit logs
Quick recommendation
- Single app / simple case: send from application code after commit
- Need reliability: use the outbox pattern
- Need scale / many writers: use CDC + webhook dispatcher
If you want, I can show you:
- a PostgreSQL trigger + outbox example,
- a Node/Python worker that sends the webhook, or
- a serverless approach for your specific database/cloud.