Webhooks that can't lie
about your data.

CommitCourier writes the outbound webhook in the same Postgres transaction as your business change — so a webhook is sent if and only if the write committed. Then it delivers with signing, retries, a DLQ, a full ledger and SSRF protection.

npmnodelicensepostgres
Everything on this page is real. Nothing here is mocked — this site is a CommitCourier consumer, installed from npm, delivering to its own receiver as you read this.
The bug

Dual-write inconsistency

Updating state and sending a webhook are two actions. A crash or rollback between them breaks one of two ways:

  • Phantom webhook — you enqueue first, then the transaction rolls back. The customer gets order.created for an order that never existed.
  • Lost webhook — you commit first, then the process dies before enqueuing. The order is final, the notification never fires.
The fix

Ride the transaction

The outbox row is written on your transaction handle. It commits with your data or vanishes with your rollback — dual-write inconsistency becomes impossible by construction. A background dispatcher then handles webhook-grade delivery.

No Redis. No SaaS. No extra broker. Just the PostgreSQL you already run.

Want the full breakdown — failure modes, recovery cost, and how this sits next to an existing webhook service? Why webhook delivery is hard →

New library, weighing the risk? See how it's built to be added small, tried without sending, and removed cleanly: Built for safe adoption →

60-second integration
import { Pool } from "pg";
import { postgresStore } from "commitcourier/store/pg";
import { createRelay } from "commitcourier";

const store = postgresStore({ pool: new Pool() });
const relay = await createRelay({ store });

// Inside YOUR business transaction — commits or rolls back atomically:
await relay.enqueue(trx, {
  eventType: "order.created",
  payload: { orderId, amount },
  endpoint: { url: "https://customer.example.com/webhooks", secret },
});

How it compares

CapabilitySaaS (Svix)Queue (BullMQ)DIY outbox + pg-bossPostelCommitCourier
Rides your DB transaction
HTTP webhook deliveryDIYDIY
Signing · retries · DLQ · ledgerpartialDIY
SSRF protectionDIY
No extra infra (just Postgres)✗ (SaaS)✗ (Redis)
Maturity (self-declared)Production SaaSMatureYour codePre-alpha0.x (pre-1.0)

“DIY” means the capability is yours to write and maintain. “—” means not offered, or not documented. Cells state each project's own public docs as of July 2026 — if we've got yours wrong, tell us and we'll fix it. Ordering and delivery guarantees don't compress into a ✓, so they get straight answers on the FAQ instead.

Atomic enqueue

Rides your DB transaction (fail-closed).

Background delivery

Polling dispatcher, fail-open. SKIP LOCKED gives one claim per row across instances — the delivery itself is still at-least-once, so dedup on the stable webhook-id.

Standard Webhooks

HMAC-SHA256 signing receivers verify with off-the-shelf tools.

Retries + DLQ

Exponential backoff with jitter; exhausted rows land in a dead-letter queue.

Delivery ledger

Every attempt recorded: status, latency, response snippet.

SSRF protection

Private / loopback / cloud-metadata ranges blocked by default.

Don't take our word for it.

Every counter on the track record was produced by this site's own dispatcher and survives restarts. The live demo drives the same relay you'd install — including the retries and the DLQ.