Outbound event delivery

Publish once.
Land it every time.

EventRelay sits between your app and every customer webhook endpoint — signing, guarding, retrying, and logging each delivery so a flaky third-party server never becomes your on-call problem.

self-hosted · Postgres + Redis · no vendor lock-in

YOUR APIPOST /eventsSSRF GUARD + SIGN✓ DELIVEREDbilling-serviceretry ×3, exponential backoffDEAD LETTERmanual resend
how one event moves through eventrelay — validate → sign → deliver → retry → dead-letter → resend

What actually happens to your event

Every delivery, accounted for.

Not a black box — three things every webhook system should show you, and usually doesn't.

SSRF-guarded, every hop

Destinations are re-validated against a private-IP and metadata-endpoint blocklist before the first request and again before every redirect it follows.

delivery.log
resolving webhook.customer.com…
✓ ok — 34.117.x.x is routable
redirect → 169.254.169.254
✕ blocked — link-local address

Signed and deduplicated

Every payload carries an HMAC signature your receiver can verify. Send the same idempotency key twice and get the same event back, not a duplicate.

request headers
X-EventRelay-Signature:
  t=1755, v1=8f21e0…
X-Idempotency-Key:
  order-8841-created

A dashboard, not just logs

Every event and every attempt against it, searchable — status, response code, and how many times it's been retried.

recent events
TypeStatus
order.createddelivered
invoice.paiddelivered
user.updatedfailed

When the other side breaks, not you

Built for endpoints that go down.

Customer webhook servers time out, redeploy, and rate-limit you at the worst moment. EventRelay assumes that's normal and plans around it.

Dead-letter, then one click

After three exponential-backoff attempts, a delivery moves to dead-letter and alerts you — resend it the moment the endpoint's back.

event #4821 — attempts
attempt 1 — HTTP 503
attempt 2 — timeout
attempt 3 — dead_letter

Rate-limited API keys

Each key gets its own rolling per-minute limit and a visible prefix, so you can revoke one without rotating them all.

api keys
production
er_live_9f2a…
120/min
staging
er_live_1cd8…
60/min

Secrets you can't leak twice

A signing secret is shown once, at creation. The database only ever holds an encrypted copy — there's nothing for a dump to expose.

new subscription
shown once — save it now
whsec_3f8a1c…e02b
01 — INGEST

Accept the event

An idempotency key on the request means retries from your side never fan out twice.

02 — GUARD

Validate the target

DNS-resolve the destination and reject anything private, loopback, or link-local.

03 — DELIVER

Sign and send

HMAC-sign the payload and POST it, following redirects one re-validated hop at a time.

04 — RECOVER

Retry or dead-letter

Exponential backoff for three attempts, then a logged dead-letter with a manual resend.

Three lines to your first delivery.

curl -X POST /events -H "X-API-Key: er_live_…" -d '{"type":"order.created","data":{...}}'