POST/PATCH/DELETE on endpoints and sources) and the redeliver and replay
routes require a secret API key. Duraton seals every signing secret at rest for you.
See the webhooks guide for what produces these rows. Endpoints and
sources can also be managed from the console’s Webhooks view; both paths write the same rows.
Listing deliveries
GET /webhook-deliveries accepts these query parameters:
Paging is keyset over
(createdAt, id) - the same model as GET /runs:
the response carries up to limit deliveries plus an X-Next-Cursor header when more exist; the header is
absent on the last page.
- TypeScript
- REST API
One delivery and its attempts
GET /webhook-deliveries/{id} returns the delivery above plus an attempts array - the append-only log
of every POST Duraton made, which is the per-attempt detail the console’s delivery inspector shows.
A wrong-project id reads back as 404.
- TypeScript
- REST API
Redelivering a delivery
POST /webhook-deliveries/{id}/redeliver re-queues a delivery for an immediate fresh attempt and returns
204. It keeps the existing attempt log and grants a new retry budget, so Duraton signs and POSTs it
again. Use it to re-send a delivery that exhausted its retries, dead-lettered on a non-retryable
response, or already succeeded (a manual re-send).
A delivery that is currently in flight (delivering) cannot be redelivered - the call returns 409 so a
manual redeliver never races an in-flight attempt. A missing or wrong-project id returns 404.
The inbound delivery log
Every POST to a source’s receive URL is recorded as an inbound source delivery, alongside its admission outcome - the received-side counterpart to the outbound delivery log above. A verified delivery can be replayed to re-ingest its stored body.GET /webhook-source-deliveries accepts these query parameters, all optional; omit them for a
workspace-wide listing:
Paging is keyset, the same model as
GET /webhook-deliveries: the response carries up to limit
deliveries plus an X-Next-Cursor header when more exist.
A status outside the accepted set (ingested, deduped, unauthorized, invalid, too_large,
misconfigured) returns 400 naming the accepted values, rather than an empty page - so a typo is a
loud error, not a silently empty result.
- TypeScript
- REST API
status is the original admission outcome of that POST, frozen at ingest - a later
replay does not rewrite it (per-replay outcomes live in attempts[]):
One inbound delivery and its attempts
GET /webhook-source-deliveries/{id} returns the delivery with its request headers, the stored body
(present only for a delivery that verified), and an append-only attempts log. Attempt 1 is the initial
ingest (trigger: initial); each manual replay appends an attempt (trigger: replay) recording the
actor who triggered it. A wrong-project id reads back as 404.
eventId (the event the admission emitted) and runId (a run it woke),
so you can pivot from a delivery to its event to a run. Both are omitted when the admission produced
neither - a deduped or rejected post, or an ingest that matched no workflow - as on the replayed attempt
above.
The delivery’s top-level status is the original admission outcome and is frozen: a replay never
rewrites it, so ?status=ingested still returns a delivery after it has been replayed. Each replay’s own
outcome lives only in its attempts[] row - the attempt above ingested first, then deduped on replay,
while the delivery stays ingested.
- TypeScript
- REST API
Replaying an inbound delivery
POST /webhook-source-deliveries/{id}/replay re-ingests the stored body and returns 200 with the
replay outcome - what re-ingesting the body produced - as { deliveryId, status, eventId?, runId? }. It
requires a full-access key. Only a delivery that originally verified (ingested or deduped) is
replayable - a rejected delivery stored no verified body. The replay outcome is recorded as a new
attempts[] row on the delivery; the delivery’s top-level status stays the original admission outcome.
Replay does not re-check the signature (the delivery was verified when it arrived, and its signed
timestamp would now be far outside the tolerance window). It re-runs the source’s current dedupeKey
and event mapping over the stored body, so within a dedupe window a replay dedupes exactly as a real
provider redelivery would. See the guide for the
replay semantics, including that a replay re-triggers downstream workflow effects.
- TypeScript
- REST API
The inbound source-delivery routes are exposed by the TypeScript SDK’s
duraton.webhooks.sourceDeliveries surface (list, listAll, get, replay).Managing endpoints and sources
GET /webhook-endpoints and GET /webhook-sources return the outbound subscriptions and inbound
sources for the project. Both omit the signing secret entirely - it is sealed at rest and never
leaves Duraton in a read.
Subscribable event kinds
An endpoint’seventKinds is the set of lifecycle kinds it subscribes to. Delivery is
subscription-gated: an endpoint only receives a kind it explicitly subscribed to, so a payload never
arrives for a kind you did not ask for. Every kind fires on a terminal transition - a run reaching a
final state, or a step settling for the last time.
The
step.* kinds are per-step: subscribe to them to track a run’s progress step by step instead of
waiting for the whole run to finish. They fire only on a step’s terminal transition (not on intermediate
retries). custom is not subscribable - it is produced by a workflow’s ctx.webhook.send, not by
subscribing an endpoint.
Creating
POST /webhook-endpoints creates an outbound subscription; POST /webhook-sources creates an inbound
source. The response includes the freshly generated secret once - store it now; it is never
returned again. Supplying your own secret adopts it instead of generating one.
A duplicate endpoint
url for the same app, or a duplicate endpoint name within the project,
returns 409.
The receive URL is not an input: Duraton always issues a 128-bit random, unguessable token for it
(immutable after create) and returns the full URL as
receiveUrl on the created source. A duplicate
source name within the project returns 409.
Editing, rotating, deleting
PATCH accepts a partial body - omitted fields are left unchanged. Set rotateSecret: true to issue a
new signing secret; the response then carries the new secret once (it is absent on an edit that did not
rotate). DELETE removes the config and returns 204. A wrong-project id returns 404.
Endpoint delivery stats
GET /webhook-endpoints/stats rolls up the delivery log per endpoint so the console can show delivery
health without a stored health field. An optional since (RFC3339) bounds the window; absent, it
defaults to the last 30 days.