Skip to content

Webhook endpoints

Register URLs to receive job events instead of polling, and read back what was delivered. At most 10 endpoints per account. Every delivery is signed with the endpoint's secret and retried until your receiver answers 2xx or 24 hours have passed; the events, their bodies and how to verify a signature are on /docs/webhooks.

8 endpoints under /api/v1/webhook-endpoints. What every call shares is on the REST overview, and what they are for — with the calls for every SDK, the CLI and MCP — on Webhooks. * marks a required field.

List webhook endpoints

GET /api/v1/webhook-endpoints

Every endpoint registered on this account, newest first. There are at most 10, so the list is not paged. The signing secret is masked (secretHint); it is returned in full only by create and rotate-secret. An endpoint the service switched off after repeated failures reads enabled: false with disabledAt and disabledReason.

Returns 200 — data is WebhookEndpoint[]

Register a webhook endpoint

POST /api/v1/webhook-endpoints

Accepts an Idempotency-Key

Registers an https:// URL to receive job events, and answers 201 with the endpoint.

The response is the only place the signing secret is ever readable (secret). Store it; a lost secret is rotated, not recovered. Every delivery carries ImageStep-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256("<t>.<raw body>", secret): compute it over the raw body, compare in constant time, and reject a t outside your tolerance window.

Omit events, or send [], to receive every job-level event; per-item events are opt-in.

The URL must be on port 443 — the only port deliveries can reach — and resolve to a public address. The address is checked now and again before every delivery attempt, against what the host resolves to then, and redirects are not followed.

Request body — EndpointRequest

Returns 201 — data is WebhookEndpoint

Registered and enabled. The one answer that carries secret in the clear.

Errors

StatusCode and when
400
  • invalid_param on url — missing
  • invalid_param on url — too long, not https://, a port other than 443, no host, or the host does not resolve to a public address (loopback, private, link-local and the other non-public ranges are refused)
  • invalid_param on events — a type that is not a subscribable event; details.received is the one refused
  • invalid_param on description — too long
422

resource_limit_exceeded — the account already has 10 endpoints; delete one first

Get one webhook endpoint

GET /api/v1/webhook-endpoints/{id}

One endpoint, its secret masked. enabled, disabledAt, disabledReason and consecutiveFailures say whether events are reaching it; the deliveries themselves are GET /api/v1/webhook-endpoints/{id}/deliveries.

Parameters

NameInTypeDescription
id (required)pathstringThe endpoint's id (whe_…), as create and the listing return it

Returns 200 — data is WebhookEndpoint

The endpoint

Errors

StatusCode and when
404

not_found — no webhook endpoint with this id on your account

Update a webhook endpoint

PUT /api/v1/webhook-endpoints/{id}

Accepts an Idempotency-Key

Every field is optional; only what you send changes. A new url is checked as on create.

  • events: [] goes back to the default subscription, every job-level event.
  • enabled: false pauses the endpoint: nothing new is queued for it, and events already queued are closed as FAILED (endpoint disabled) rather than held.
  • enabled: true turns it back on and also clears an automatic disable — disabledAt, disabledReason and consecutiveFailures. POST /{id}/test is the cheap way to check the receiver first.

Parameters

NameInTypeDescription
id (required)pathstringThe endpoint's id (whe_…), as create and the listing return it

Request body — EndpointRequest

Returns 200 — data is WebhookEndpoint

The endpoint as it now is

Errors

StatusCode and when
400
  • invalid_param on url — too long, not https://, a port other than 443, no host, or the host does not resolve to a public address (loopback, private, link-local and the other non-public ranges are refused)
  • invalid_param on events — a type that is not a subscribable event; details.received is the one refused
  • invalid_param on description — too long
404

not_found — no webhook endpoint with this id on your account

Delete a webhook endpoint

DELETE /api/v1/webhook-endpoints/{id}

Accepts an Idempotency-Key

Deletes the endpoint and answers 204 with no body. Nothing more is delivered to it: events still queued are closed as FAILED (endpoint no longer exists), and its delivery history is no longer readable. To stop deliveries and keep the endpoint, PUT it with enabled: false.

Parameters

NameInTypeDescription
id (required)pathstringThe endpoint's id (whe_…), as create and the listing return it

Returns 204 — no body

Deleted

Errors

StatusCode and when
404

not_found — no webhook endpoint with this id on your account

Recent deliveries to an endpoint

GET /api/v1/webhook-endpoints/{id}/deliveries

Paged — page and perPage, or cursor, in; meta out

Newest first, with the attempt count, the HTTP status your receiver answered and the next scheduled retry. This is where to look when an event did not arrive. A row does not carry the event body — that is the document your receiver was sent, and eventId is what joins the two. Test deliveries are listed too. Records are kept 14 days.

Parameters

NameInTypeDescription
id (required)pathstringThe endpoint's id (whe_…), as create and the listing return it

Returns 200 — data is WebhookDeliverySummary[]

One page of deliveries

Errors

StatusCode and when
404

not_found — no webhook endpoint with this id on your account

Rotate the signing secret

POST /api/v1/webhook-endpoints/{id}/rotate-secret

Accepts an Idempotency-Key

Replaces the signing secret and returns the new one in the clear, once (secret). The old secret stops verifying immediately: every attempt from now on — retries of events queued before the rotation included — is signed with the new one. A delivery your receiver refuses in the switch-over is retried on the normal schedule, so a receiver that answers non-2xx to a bad signature and picks up the new secret within minutes loses nothing.

Parameters

NameInTypeDescription
id (required)pathstringThe endpoint's id (whe_…), as create and the listing return it

Returns 200 — data is WebhookEndpoint

The endpoint, with the new secret in the clear

Errors

StatusCode and when
404

not_found — no webhook endpoint with this id on your account

Send a test delivery

POST /api/v1/webhook-endpoints/{id}/test

Accepts an Idempotency-Key

Posts a synthetic webhook.test event to the endpoint while you wait, signed like any delivery, and answers with the delivery record: status is DELIVERED when your receiver answered 2xx, otherwise FAILED with responseStatus and error.

  • One attempt: a failed test is not retried, and does not count towards the automatic disable. A 2xx resets the endpoint's run of failed events without re-enabling it.
  • It works on a disabled endpoint — the way to check a receiver before turning it back on.
  • The attempt waits up to 5 s to connect and 10 s for the answer, so this call can take that long.

USE THIS WHEN: verifying a new receiver, or checking signature verification. DO NOT USE WHEN: replaying a real event — this one carries no job data.

Parameters

NameInTypeDescription
id (required)pathstringThe endpoint's id (whe_…), as create and the listing return it

Returns 200 — data is WebhookDeliverySummary

The attempt was made — read status: an unreachable receiver is a FAILED delivery, not an error

Errors

StatusCode and when
404

not_found — no webhook endpoint with this id on your account

Objects

Each described once; a linked type is another object on this page.

EndpointRequest

Webhook endpoint create/update payload

FieldTypeDescription
descriptionstring
Free-text label shown in listingse.g. "Production n8n"
enabledboolean
Update only — ignored on create, where an endpoint starts enabled. false pauses deliveries; true resumes them and clears an automatic disable.
eventsstring[]
Event types to receive. Omit it, or send [], for every job-level event (job.completed, job.failed); the per-item events (job.item.completed, job.item.failed) are opt-in, because a 500-item job would otherwise be 500 deliveries nobody asked for. Any other value is 400 invalid_param.e.g. ["job.completed","job.failed"]
urlstring
Where to POST events: an https:// URL on port 443 whose host resolves to a public address. Required on create; on update, send it only to change it.e.g. "https://example.com/hooks/imagestep"

WebhookDeliverySummary

One webhook delivery attempt: did it arrive, after how many tries, and what did the receiver say

FieldTypeDescription
attemptsinteger (int32)
How many HTTP attempts have been made
createdAtstring (date-time)
When the event was queued; retries stop 24 hours after it
deliveredAtstring (date-time)
When a 2xx came back
errorstring
Why the last attempt failed, when it did: HTTP 503, a connection error, target refused: … when the host resolved to a non-public address (no request was sent), or endpoint disabled
eventIdstring
The event this delivered; the same id is in the body's id and on every retry
eventTypestring
Event type, e.g. job.completed
idstring
The delivery's id, whd_…
nextAttemptAtstring (date-time)
When the next retry is due, while status is PENDING
responseStatusinteger (int32)
The last attempt's HTTP status, or null when the request never got one (DNS, timeout, TLS)
statusstring
PENDING while retries remain, DELIVERED once a 2xx came back, FAILED when it gave up — 24 hours after the event was queued, after a test's single attempt, or when the endpoint was disabled or deleted with the event still queued

WebhookEndpoint

A URL this account receives job events at. The signing secret is masked on every read but the create and rotate-secret answers.

FieldTypeDescription
consecutiveFailuresinteger (int32)
Events in a row whose delivery ran out of retries since the last accepted one. At 5 the endpoint is disabled; any 2xx resets it to 0.
createdAtstring (date-time)
descriptionstring
Your label for it
disabledAtstring (date-time)
When the service switched the endpoint off because 5 events in a row ran out of retries; absent otherwise. PUT with enabled: true clears it.
disabledReasonstring
Why it was switched off: the count and the last delivery error
enabledboolean
Whether events are delivered. false when you paused it, or when the service disabled it (then disabledAt is set).
eventsstring[]
The subscription as you set it. Absent or empty means every job-level event; per-item events are delivered only when listed here.
idstring
The endpoint's id, whe_…
secretstring
The signing secret in the clear, whsec_… — present only on the create and rotate-secret answers, so store it then. Every other read carries secretHint instead.
secretHintstring
The secret masked — its first 7 and last 4 characters — enough to tell which one your receiver holds, not enough to sign with
updatedAtstring (date-time)
urlstring
Where events are POSTed