Webhooks

Subscribe to events, read the delivery format and verify the signature, plus retry behavior and delivery history.

Last updated 2026-10-23

Webhooks send an HTTP notification when a list, a search, enrichment, an import or a monitor changes. They are created and managed through the REST API, failures are retried, and every attempt can be inspected. This page covers subscribing, the request format, signature verification and retries.

Create a subscription

Send a POST /v1/webhooks:

{
  "url": "https://example.com/hooks/renewsell",
  "events": ["item.created", "enrichment.completed", "search.completed"]
}
  • url must be a public HTTP(S) address, not a private network or localhost, and at most 2048 characters.
  • events takes at least 1 and at most 19 values.
  • The response's secret is the signing secret, starts with whsec_, and is returned only this once; store it safely.
  • Manage subscriptions with GET /v1/webhooks, GET /v1/webhooks/{id}, PATCH /v1/webhooks/{id} and DELETE /v1/webhooks/{id}.

Delivery format

Each delivery is a POST with a JSON body:

{
  "id": "event_…",
  "object": "event",
  "type": "item.created",
  "data": {},
  "createdAt": "2026-10-23T08:30:00.000Z"
}

Headers:

HeaderContent
webhook-idEvent id, for de-duplication
webhook-timestampUnix timestamp in seconds
webhook-signaturev1, plus the signature
content-typeapplication/json

Event types

EventWhen it fires
search.createdA search is created
search.updatedSearch status or progress changes
search.completedA search completes
search.canceledA search is canceled
search.failedA search fails
item.createdA result is added to a list
enrichment.completedAn enrichment column finishes one cell
import.createdAn import is created
import.completedAn import completes
import.deletedAn import is deleted
monitor.createdA monitor is created
monitor.updatedA monitor is updated
monitor.deletedA monitor is deleted
monitor.run.createdA monitor run starts
monitor.run.completedA monitor run completes
monitor.run.canceledA monitor run is canceled

Target creation, update and deletion also send events, but those three type names still use an older naming that does not match the Target vocabulary in the app. Before subscribing, copy the exact name from the type returned by GET /v1/events, or from the events field of a webhook you already created.

Verify the signature

The signed string is <Webhook id>.<webhook-timestamp>.<raw body>. Sign it with HMAC-SHA256 using the secret returned at creation, encode the result as base64url, and prefix it with v1,. The webhook id is the id returned at creation (also visible in GET /v1/webhooks). The webhook-id header is the event id, for de-duplication; it is not part of the signature.

WEBHOOK_ID="webhook_…"
SECRET="whsec_…"
TIMESTAMP="1750000000"
BODY="$(cat payload.json)"

printf '%s.%s.%s' "$WEBHOOK_ID" "$TIMESTAMP" "$BODY" \
  | openssl dgst -sha256 -hmac "$SECRET" -binary \
  | openssl base64 -A | tr '+/' '-_' | tr -d '='

Compare the output with the part of webhook-signature after v1,. Only process the request when they match.

NOTE

Verify before processing. Drop any request whose signature does not match instead of acting on its body.

Retries

  • A 2xx response is a success and is not retried.
  • A 429, a 5xx or a network error is retried. The delay starts at about 2 seconds and doubles each time, capped at one hour.
  • Any other 4xx is not retried; that delivery is marked failed.
  • Redirects are followed up to 5 times, and every new address is re-checked as a public address.
  • Events are picked up by a background sweep, so a delivery usually arrives within a few minutes.

Inspect deliveries

GET /v1/webhooks/{id}/attempts returns the time, status code, response headers and response body (bounded) of each attempt. Filter with eventType and successful. Each delivery keeps its last 10 attempts.

Related pages