1. Register an endpoint
POST /api/webhooks takes a name, your target_url and the events[] you want:
POST /integrations/v1/api/webhooks
target_urlmust behttps://on a public host, and the same URL cannot be registered twice in one workspace (409 duplicate_rejected).events[]is an exact list of catalog types, validated one by one; a name outside the catalog is a422. There is no wildcard: subscribe to the types you handle, and add new ones when you start handling them. The catalog is in section 6.filters.account_sidnarrows deliveries to one sending identity (a sender profile’s LinkedIn account or mailbox), so an endpoint that serves one sender is not woken by the others.
secret, a 32-character hex string, exactly once. Store it: every later read answers null for it, and it is what you verify signatures with.
2. Verify deliveries
Every delivery is an HTTPSPOST with a JSON body and these headers:
The body is a fixed envelope, with the event’s own data nested under
payload (trimmed here):
payload is the event’s own row, under the field names the REST API uses for that entity: a message for messages.*, a conversation for conversations.*, the data source with its funnel counts for data-sources.*, the workflow record for workflow-records.*. The reference page of that entity is the field guide.
The signature is HMAC-SHA256(secret, "{t}.{raw_body}") over the raw request body, with the timestamp mixed in to block replays. Verify before parsing, and reject signatures older than about 5 minutes:
3. Test before relying on it
POST /api/webhooks/{sid}/test sends one signed synthetic envelope, of type webhooks.test (or the type you name in override_event_type), to your endpoint right now and answers what came back: http_status, duration_ms and error_kind (none, http_error or network). It confirms reachability, signature handling and parsing without waiting for a real event.
Three things to know about it: it writes no row to the delivery log, moves no failure counter and fires no event; it is allowed on a failed webhook (the check before you revive it) and refused on one switched off (409); and it is throttled to 10 calls a minute per webhook.
4. Retries and auto-disable
A delivery is attempted up to 5 times, with waits of 1 minute, 5 minutes, 30 minutes and 2 hours between attempts. The fifth failure is terminal, so a dead endpoint settles about 2.6 hours after the first attempt.
After 20 consecutive failed attempts across the subscription’s deliveries (a delivered event resets the counter), or a single
410 Gone, the subscription flips to failed and stops delivering: its waiting deliveries are cancelled, and the transition emits webhooks.failed, so a second webhook can page you when the first one dies.
Bringing it back is two calls: fix the endpoint, then PATCH /api/webhooks/{sid} with {"status": "on"}, which resets the counter and re-issues nothing. Then re-arm what the outage lost from the delivery log (next section): a delivery that ended failed is retried by hand, a delivery the failure cancelled is not (a cancel is final), so revive quickly. Switching a webhook off cancels its pending deliveries too, and off to on leaves the counter alone.
5. Use the delivery log
The log holds one row per event per subscription, not one per attempt: a retry updates that row in place, bumpingretry_count and overwriting the response code. The row is the delivery, and its history is the counter. A row’s status is pending, in_progress, retrying, success, failed or cancelled.
POST /api/webhook-logs/searchlists deliveries with status, response code and timing; filter by webhook, sending identity, event type, status or response code, andincludethe webhook.POST /api/webhook-logs/metricsaggregates outcomes over a requiredperiod(from,to): success and failure rates, p50, p95 and p99 latency, total retries, the last success and the last failure;group_bysplits them by status, event type, response code, webhook or account. The rates count terminal rows only, so a delivery still on its ladder sits incounts.groups.status.retryinginstead.POST /api/webhook-logs/{sid}/retryre-arms one delivery: status back topending, due now, the original envelope to the original URL,retry_countkept (it counts attempts that went out). Refused on anin_progressrow (409: cancel it or wait), on acancelledone (that was a decision) and while the parent webhook is deleted oroff(422).POST /api/webhook-logs/{sid}/cancelstops a delivery that has not settled, frompending,retryingorin_progress. On anin_progressrow the attempt in flight may still land as a success, which wins over the cancel; the answer says so within_flight_attempt_pending: true, so read the row again.
retry and cancel take an optional expected_status, a compare-and-set guard: when the row moved since you read it, the call answers 409 invalid_transition instead of acting on a state you did not see.
6. The event catalog
33 types across the producing services, each a business fact an integrator can act on, never a per-row progress tick (a 5,000-row import tells you once, withdata-sources.completed, not 5,000 times).
The same list is the
events enum on the create-webhook schema, which is where a generated client or an agent reads it from.