SettleMint
ConceptsEvents out

Webhook Delivery

Webhook Delivery retries a signed notification for three days and records whether a counterparty was told, so an integrator skips its own retry logic.

One event, recorded attempts, consumer deduplication.The platform records the event first, and the delivery machinery works from that row rather than from memory. It then signs an envelope, whose headers follow the Standard Webhooks scheme, giving the message an identifier, a signature and a timestamp, with the platform adding its own delivery identifier and endpoint identifier beside them. The platform leases the row for the attempt, and the POST and its database record are separate effects. A crash frees the lease, and the next scan reclaims the row: a send whose outcome was never recorded can repeat. Each attempt has twenty seconds to finish, and one of three outcomes follows. A 2xx reply means the consumer accepted the message and the delivery ends there. A retryable failure goes back on an exponential curve that starts near thirty seconds and doubles, with the platform multiplying each delay by a random factor between 0.7 and 1.3. A terminal failure stops at once because repeating it cannot help, meaning a 4xx reply other than 408 or 429, or a counter-signed receipt that fails its signature or hash check. Only the retry branch keeps going until it hits the three-day boundary: retrying stops three days after the platform recorded the event, and the platform writes that abandonment as a warning log and a failed span rather than dropping the event silently.One event, recorded attempts, consumer deduplication.The same figure as the desktop variant, drawn portrait for phone widths. The platform records the event before it signs an envelope with the Standard Webhooks headers plus its own delivery and endpoint identifiers. The leased attempt is the focal step. Recovery reuses recorded outcomes, but an unrecorded send can repeat. Each attempt has twenty seconds, and one of three outcomes follows: delivered on a 2xx reply, retried on a jittered backoff curve starting near thirty seconds, or terminal on a 4xx other than 408 or 429, or a failed counter-signed receipt. Only the retry branch keeps going until it hits the three-day boundary, and the platform logs that abandonment instead of letting it pass unnoticed.

Recorded attempts support recovery. A response loss or crash before recording can still cause a duplicate; deduplicate by webhook-id.

A brief outage at the counterparty does not cost it the notification: a retryable failure gets three days of jittered backoff, starting near thirty seconds, before the platform gives up.

A 4xx other than 408 or 429 is terminal, and so is a counter-signed receipt that fails its signature or hash check: the platform stops rather than spending an attempt on a rejection it cannot fix.

Recovery reuses recorded attempts, so a crash rarely resends what already worked. A POST the receiver got before the platform recorded it can still arrive twice, which is why webhook-id is the field to deduplicate on.

A notice the counterparty can build on

Webhook Delivery gives an external counterparty a signed, durable record that a platform event reached its system. The platform records every delivery attempt and retries the failures worth retrying. The receiving system verifies the signature, deduplicates on the message identifier, and handles the event's finality state.

The platform keeps trying for three days, from the moment it recorded the event. Delivery can resume if the consumer recovers before that retry window expires. Events can be abandoned after the deadline or a terminal rejection. That clock starts at recording, not at the first attempt. An event that waited in a queue before anyone tried to deliver it has less than three days of retrying left, worth knowing before promising a consumer a full replay window.

The platform signs each message, so the receiving system can establish two things without trusting the network: that the platform sent it, and that nothing altered it in transit. A valid signature proves message authenticity and integrity. Check the event's finality state and your reconciliation requirements before acting on it.

Failure stays loud. Once retrying stops, the platform writes the abandonment as a warning in its logs and marks the operation failed in its traces, naming the event and why it gave up. An operations team finds abandoned events there, not by guessing.

Why a notice needs its own delivery system

A Platform Event is a fact the platform has already recorded. Left there, it tells nobody outside the platform anything until somebody looks. The alternative to a delivery system is a tenant's own poller, calling the platform's read APIs on some interval and diffing the answer against what it saw last time. The other option is a bare callback URL with no retry, no backoff, and no record of what was sent when a request timed out. Both push the retry logic, the endpoint health tracking, and the proof of delivery onto the tenant.

Webhook Delivery keeps that work on the platform side instead. It owns the retry schedule, the per-endpoint circuit breaker, and the durable row an operator or an auditor reads back when a counterparty later disputes whether it was told. That record is what turns "we believe we sent this" into evidence, covered in full under Evidence that the notice went out.

The contract the tenant gets is at least once, never exactly once. A crash or a lost response can still cause a resend, which is why the platform hands over a message identifier the receiving system can check before it acts. Delivery owns getting the notice there; deduplicating it is the one piece of the work only the receiving system is positioned to do, covered under Receiving, verifying and answering.

The circuit breaker that watches endpoint health

Webhook Delivery gives every registered endpoint its own circuit breaker, a small state machine with three states: closed, open and half-open. It exists so one endpoint that starts failing does not consume delivery capacity meant for every other consumer, and so recovery happens automatically instead of waiting on an operator to notice and re-enable it.

Thirty consecutive failed deliveries open it, which pauses live sends to that endpoint. A background probe the platform runs on its own schedule then checks it every thirty seconds, waiting at least sixty seconds between attempts on the same endpoint. Two of those probes succeeding in a row moves the breaker to half-open. Live deliveries resume there: one further successful delivery closes the breaker and resets the failure count to zero, while a single failed probe sends it back to open. The platform records each state change as it happens.

Recovery is automatic, and a failed probe can send it back to open.An endpoint's circuit breaker holds one of three states: closed, open, or half-open. Closed delivers normally and resets the failure count on every success. Thirty consecutive failed deliveries move it to open, which pauses live sends to that endpoint. While open, the platform runs its own background probe on a fixed schedule the endpoint owner does not control, and two of those probes succeeding in a row moves the breaker to half-open. Live deliveries resume at half-open: one further successful delivery closes the breaker and resets the failure count to zero, while a single failed probe sends it back to open. A manually triggered test event is excluded from every step of this cycle, so probing an endpoint by hand can never open, advance, or close its breaker.Recovery is automatic, and a failed probe can send it back to open.The same figure as the desktop variant, drawn portrait for phone widths. Closed delivers normally and resets its failure count on every success. Thirty consecutive failures move it to open, which pauses live sends and starts the platform's own background probe on a fixed schedule. Two of those probes succeeding in a row moves it to half-open, where live deliveries resume. One further successful delivery closes the breaker completely; one failed probe sends it back to open. A manually triggered test event never advances this cycle.

The platform probes an open circuit breaker. Successful probes admit live traffic through half-open before normal delivery resumes.

The platform deliberately keeps test events out of that accounting. An operator can fire a test event at a disabled or recovering endpoint to see what happens. The result moves neither the breaker nor the failure streak, and it neither replaces nor speeds up the platform's own automatic probes. Probing an endpoint by hand therefore never touches its health record.

When a delivery fails, the platform records what kind of failure it hit, and the kind is the useful part. Failing to resolve the address counts as a DNS failure. A broken security handshake counts as a TLS failure. A connection that never opened times out at the connection stage, and a reply that never arrived times out waiting to read it. A reply with no recognisable HTTP status counts as an invalid response. All five of those, plus a 5xx status and a 408 or 429 reply, retry on the normal curve. Only two kinds of outcome are terminal: a rejection by the consumer, meaning any 4xx other than 408 or 429, and a failed counter-signed receipt, whether the signature or the hash is wrong. Sending the identical request again would only reproduce the identical rejection, so the platform does not try. Reading the failure kind tells an operator whose problem it is, and whether the platform is still trying, before anyone opens a log.

Recovery reuses recorded attempts

Sending a webhook and recording its outcome are separate effects. A process can stop after the consumer receives the POST but before the attempt record is written. Recovery may repeat that send.

The leased attempt uses the endpoint, event, and attempt number to find an existing delivery record. When that record exists, recovery reuses its outcome. When it does not, recovery sends the request. This reduces repeated sends but does not provide exactly-once delivery.

Registering or updating an endpoint runs an address check up front. The URL must use HTTPS and resolve only to a public address, so a private, loopback, or link-local target is refused before it is ever saved. Register the address the consumer is reachable at from the public internet; a private-network deployment that needs an internal destination allows specific addresses at the deployment level instead, covered in the FAQ below.

The platform also pins the request to one address at send time. It resolves the endpoint's host, validates the address it got back, then connects to that address directly, so no second name lookup can redirect the request after the check passed. The original host name still travels in the Host header and in the security handshake, so virtual hosting and certificate checks behave normally.

The platform follows no redirects. An endpoint that answers with one is recorded as an unrecognised reply and retried on the normal curve. Register the address the redirect points to, not the one that issues it, and the retries stop landing on a reply the platform cannot use.

It also reads at most eight kilobytes of the reply, because it stores that reply on the attempt record and will not let a consumer decide how much it keeps.

Payload shape is also a registration choice. Thin is the default, and it strips personally identifying fields from every payload before it leaves the platform. Fat includes them, but only after the endpoint owner acknowledges, field by field, exactly which personal fields its current subscriptions will expose. The platform computes that list from what the endpoint is subscribed to, and it refuses the switch until every field on the list is acknowledged. An endpoint cannot be created fat outright: register it thin, then move it to fat once the fields it would expose are known.

Signatures get refreshed rather than reused indefinitely. Before an attempt, the platform re-signs any envelope whose timestamp has reached four minutes old. The library consumers use to verify accepts a five-minute spread between that timestamp and their own clock, so re-signing at four minutes leaves a margin and a message that waited in a backoff does not arrive already expired. While a signing secret rotates, the platform signs with both the active secret and the previous one and sends both signatures, so a consumer that has adopted either can verify.

Receiving, verifying and answering

Webhook Delivery reaches a consumer as a single POST request with a JSON body and a small set of headers. How that consumer answers decides everything that happens next: whether the platform marks the event delivered, retries it, or gives up for good. Three of those headers follow Standard Webhooks, an open scheme with off-the-shelf verification libraries in most languages: webhook-id identifies the message, webhook-signature holds the signature or signatures, and webhook-timestamp gives the signing time in whole seconds. The platform adds two of its own, webhook-delivery-id for this individual attempt and webhook-endpoint-id for the endpoint it addressed.

Verify with a Standard Webhooks library rather than by hand, and let it reject a timestamp outside its tolerance. webhook-signature can hold more than one signature, separated by a space, while a secret rotates. Verifying against any one of them is enough. Keep the receiver's clock synchronized. Queueing and network delay consume part of the verifier's timestamp tolerance.

A duplicate reaches you in two situations. The first is a retry of a delivery whose outcome the platform never confirmed. The second is a consumer that did the work but did not reply inside the twenty-second attempt window, so the platform retries a request you already fulfilled. Treat webhook-id as the idempotency key: a retry repeats the same webhook-id as the attempt that failed, while webhook-delivery-id changes on every attempt. Record which message identifiers you have already handled and return success for one you recognise. Store the handled identifier and the resulting business change atomically, or use an equivalent idempotent processing design.

Your reply decides what happens next.

Your replyWhat happens nextSend it when
Any 2xxDelivery ends. Marked delivered.You received the message and are done with it.
A 4xx other than 408 or 429Stops at once. Nothing retries.You will never accept this exact message.
408 or 429Retries on the normal curve.Rate-limiting or not ready; the platform treats it as a retryable client failure.
A 5xxRetries on the normal curve.Something broke on your side; the platform treats it as a server failure.
No reply inside twenty secondsCounted as a failure, retries like a 5xx.Never on purpose. Acknowledge first, do the work after.

An endpoint can opt into counter-signed receipts. A 2xx reply only shows that the platform reached you and you answered. A counter-signed receipt goes further: the consumer signs a hash of the exact content with its own key, to provide evidence linking the receipt to that payload. Reach for it on the flows where that dispute would be expensive, not on every endpoint. Turning it on trades retry safety for proof: a receipt that fails its signature or hash check ends the delivery for good instead of retrying, and the consumer has at most one minute to get it right. The confirmation deadline defaults to thirty seconds, and the platform clamps it between one second and one minute, so a value outside that range moves back inside rather than creating an unmeetable deadline.

Evidence that the notice went out

Webhook Delivery keeps a durable record of every attempt it makes toward a consumer, not just the one that finally succeeds. A dispute about whether or when a counterparty was notified has a recorded answer instead of a guess. Every attempt leaves a row with the attempt number, the status the consumer returned, its response headers, and the first part of its body. Where the attempt failed, the row also records the failure classification and the trace identifier tying it to the wider operation.

Delivery also appends to the event's own chain of hop hashes, recording a dispatch-attempt hop with a hash of the exact payload it sent and the endpoint it signed for. That distinguishes "we believe we sent this" from "this specific content went out", which matters when a counterparty disputes what they received.

The platform records permanent loss on purpose. Once retrying stops, it writes a warning naming the attempt number, the failure class and the age of the event, and it closes a failed span under the same trace. Operators with access to those records can investigate abandonment. Retain and monitor them under your incident and audit policy.

The platform versions its signing keys. It records which secret versions signed a successful delivery, so the question of which key signed a disputed message keeps a recorded answer even after that secret has rotated out.

Frequently asked questions

Can a private-network deployment point a webhook at an internal address?

Not from the endpoint form itself; the public-address check applies to every organisation the same way. A private-network installation can allow specific internal destinations at the deployment level, which is a decision an operator makes for the whole install, not one an organisation sets by editing its own endpoint URL. If you are integrating against a private-network deployment and a legitimate internal address keeps getting refused, that allowance is what to ask the operator about.

What happens to deliveries already in flight when I change an endpoint's URL?

Nothing in flight retargets silently. If the endpoint still has pending deliveries, a plain URL change is refused. You either wait for those deliveries to finish, or resend the update with an explicit acknowledgment that pending attempts will now retry against the new address. A previously completed attempt keeps its original destination. Each attempt reads the endpoint's current URL at send time, so a delivery still mid-retry when you switch lands on the new address from its next attempt onward.

How do I submit a counter-signed receipt?

Sign the delivery's identifiers and your own hash of the payload you received with the endpoint's webhook signing secret, using the dalp.whr.v1 scheme, and post the result to the webhook API before the receipt window closes. The guide has the exact string to build and sign and the field names the request expects. A hash that does not match what the platform sent, or a signature that fails to verify, ends the delivery at once; neither retries. Resending with the same Idempotency-Key returns the receipt you already created rather than a duplicate.

  • Transaction Lifecycle: The states an instruction passes through inside the platform. A webhook is how an outside system hears about those state changes; it is not itself one of them.
  • Platform Event: The published vocabulary of things that happen and what a consumer may rely on. The event is the fact; delivery is the transport that carries it.
  • Audit Trail: The wider model that per-attempt rows, hop hashes and trace identifiers belong to. Read it for how delivery evidence fits alongside every other record the platform keeps.

On this page