Skip to content
The foundation under itDeveloper platform

Outbound webhooks

Signed events another system can subscribe to. Three exist today.

Growth-OS can post a signed event to an endpoint when something changes. Three event types are built: an organization profile change, a subscription change, and a completed data export. No growth record emits one, so nothing in our daily work depends on this. When we do want another internal tool to hear about something, the delivery, retry and replay machinery is already here.

HMAC-SHA256signature on every event sent8delivery attempts before it stops30 daysof history that can be replayed

Product example

See it working

This is the same visual component used by the authenticated app, rendered with deterministic sample data.

Scenario
viewport
surface theme
mode

Delivery history

A signed delivery that succeeded next to a dead one that can still be replayed.

Inert sample data · no network access

Outbound webhooks

1 enabled

Each endpoint receives signed, versioned events over HTTPS with bounded retries.

Production receiverEnabled
https://example.com/hooks/saasorganization.updatedsubscription.updatedLast delivery Aug 13, 2026, 8:00 AM

Add an endpoint

The signing secret appears once, right after you add the endpoint.

The receiver has to answer within the retry window.

Events

Only the events you pick are sent to this endpoint.

Recent deliveries

1 need attention

Every event sent to an enabled endpoint, with the attempts it took. Failed and stopped deliveries can be replayed during the retention window.

EventStatusAttemptsResponseCreatedAction
subscription.updatedDelivered1 attemptHTTP 200Aug 13, 2026, 7:59 AMNothing to replay
organization.updatedReceiver unavailable after the bounded retry policy.Stopped retrying8 attemptsHTTP 503Aug 13, 2026, 7:30 AM

Preview ready

Highlights

Why it matters

Queued with the change itself

An event is written in the same transaction as the change that caused it. If the change rolls back there is no event; if it commits the event cannot be dropped.

Retries with patience

A receiver that is down gets up to 8 attempts with growing delays, then the delivery stops cleanly instead of looping.

A real answer to whether it sent

Every attempt records its status and a short response snippet, and an admin can replay a failed delivery for 30 days.

The problem this solves

Without a shared foundation, every team rebuilds these edge cases, differently.

The event that never arrived

A receiver was down for a deploy and the event was sent inline with the request, so it simply died. No record, no retry, nothing to resend. Queueing the delivery with the change removes that whole category.

No way to prove it was us

Without a signature and a timestamp, a receiver cannot tell a real event from a forged one. Every delivery here is signed with the endpoint secret and carries the timestamp it was signed at.

Searching logs for a maybe

Someone asks whether an event went out and the only answer is a log search and a guess. With attempt history you see the failed status on attempt three and press replay.

How it works

The mechanism end to end, as implemented in the repository.

  1. 1

    Events queue with the write

    enqueueWebhookEvent runs inside the owning transaction, validates the payload against that event type and version, and inserts a delivery row per matching enabled endpoint.

  2. 2

    A worker signs and sends

    A background worker decrypts the endpoint secret, signs the body with the timestamp, and posts under a strict timeout.

  3. 3

    Every attempt is recorded

    Each try is claimed under a row lock before any network call, so it gets a durable attempt number. Only timeouts, conflicts, rate limits and server errors retry.

  4. 4

    Dead deliveries wait for a person

    After 8 attempts or 30 days a delivery is marked dead with its last error kept, and an admin can replay it through the same worker.

Engineering guarantees

What this does and does not promise

Safety boundary

Only event types that are actually built are advertised; draft definitions stay hidden. Delivery history shows status codes and short response snippets, never a signing secret. Today the three built events are all platform events, not growth records.

Implementation evidence

The boundary above is code, not a claim. These files carry the contract:

  • src/registries/webhook-events.ts
  • src/features/developer-platform/delivery.ts
  • src/features/developer-platform/jobs.ts
  • src/features/developer-platform/crypto.server.ts
Questions

Frequently asked questions

What outbound webhooks rests on

The docs describe the same contracts this page demonstrates.