Webhooks
Subscribe a URL to deal events and NeoKivo POSTs a signed payload as they happen. The event catalog, delivery and retry behavior, and what happens when a webhook stops working.
Updated 2026-07-06
A webhook is a URL you register in Settings that NeoKivo calls when a deal changes. It's the fastest way to react to pipeline events from outside NeoKivo — no polling required.
The event catalog
| Event | Fires when |
|---|---|
| deal.created | A new deal is added |
| deal.stage_changed | A deal moves to a different pipeline stage |
| deal.won | A deal is marked won |
| deal.lost | A deal is marked lost |
These four are the whole public vocabulary today. Reopening a deal (won or lost back to open) does not emit anything.
Subscribe to a webhook
- 1Open Settings → Integrations. Only the workspace owner can create, delete, or re-enable a webhook.
- 2Under Webhooks, set up a subscription: an https:// URL, one or more events, and a format — plain JSON, or a Slack-compatible message for an incoming-webhook channel URL.
- 3Copy the signing secret. It starts with nk_whsec_ and is shown only once.
URLs must be https — the payload carries deal data and is worth protecting in transit. Up to 10 subscriptions per workspace.
Delivery semantics
- Every delivery is signed with HMAC-SHA256 over the raw body — see verify webhook signatures for the exact scheme and a verifier.
- Each attempt times out after 10 seconds. Any 2xx response counts as success; anything else, a timeout, or a redirect counts as a failure (redirects are never followed).
- A failed delivery retries with backoff: roughly 30s, 1m, 2m, 4m, 8m, 16m, 32m, then 64m — 8 retries, about 2 hours of attempts, before that delivery is exhausted.
- Delivery is at-least-once, not exactly-once: if a batch of deliveries is interrupted partway through, the retry re-sends to subscriptions that already got a copy.
- Deliveries are not ordered. Sort by occurredAt in the payload, not by arrival time.
X-Neokivo-Delivery identifies a delivery attempt, not the underlying event — a duplicate delivery of the same event carries a different id. If you need idempotency, key on the payload's content instead: (event, data.deal.id, occurredAt).
Disabled webhooks
After 20 consecutive deliveries exhaust every retry with no success in between — roughly 180 failed requests over several days to a truly dead endpoint — the subscription disables itself and you get a webhook.disabled notification, addressed to the webhook's creator if they're still a member, or to every workspace owner if not.
The same thing happens immediately, independent of that counter, if the subscription's creator leaves the workspace: deliveries stop rather than falling back to workspace-wide visibility. This is the same fail-closed rule API keys and the MCP server follow.
Re-enabling a disabled webhook is owner-only, from Settings. It resets the failure count, and makes the re-enabling owner the subscription's new creator — deliveries from then on are scoped to what they can see.
The payload
Every delivery is a versioned envelope: the event name, when it happened, the workspace, and the deal — plus the from/to stage names on a stage change. See verify webhook signatures for the full envelope shape, a real sample, and how to check the signature before you trust the body.
Spotted something out of date? Email hello@neokivo.com.