Overview

kurnl sends HTTP POST requests to your webhook_url after significant events. The webhook_url lives on your provider integration settings (tenant-config); the signing secret is the Shared webhook secret from the dashboard Checkout page.
Webhooks are inert until webhook_url is configured — events are silently skipped, with no error. And if no webhook secret is set, deliveries arrive unsigned (no X-Webhook-Signature header): configure a secret and reject unsigned requests.
All events share a common envelope structure with event-specific fields merged in at the top level.

Event reference

What is not emitted: there is no subscription.suspended event, and subscription.activated does not fire on dashboard-driven activation, the kurnl-billed Stripe flow, or the home-drop / confirm-install path — today those emit nothing. Also mind the payload variants: subscription.activated from a provider switch carries only subscription_id, started_at, via — don’t key on location_hash unconditionally.

Payload structure

All payloads include event, provider_document_id, and timestamp. Event-specific fields are merged in alongside them.

Verifying signatures

When you have a webhook_secret configured, kurnl signs every request body with HMAC-SHA256 and sends the signature in the X-Webhook-Signature header as sha256=<hex_digest>. Always verify this signature before processing any event.

Delivery behaviour

  • kurnl first attempts delivery up to 3 times inline, with exponential back-off (2s, then up to 30s between retries) and a 15 second timeout per attempt
  • If all inline attempts fail, the event moves to a dead-letter queue: a scheduler (every 10 minutes) redelivers it with growing back-off until 5 total attempts have been made, then the event is dropped and kurnl operations is alerted
  • Every delivery carries an X-Webhook-Event header with the event name; DLQ redeliveries additionally carry X-Webhook-Retry: true — use it for dedup awareness
  • Network errors and 5xx responses are retried; 4xx responses are not retried (a 4xx during DLQ redelivery ends retrying immediately)
  • Return any 2xx status to acknowledge — kurnl does not inspect the response body

Idempotency

kurnl may deliver the same event more than once after transient failures. Use job_id or subscription_id as an idempotency key to deduplicate in your handler:

Testing webhooks locally

During development you can use a tunnelling tool to expose your local server to kurnl:
Alternatively, use webhook.site for a no-setup temporary receiver to inspect payloads without running any code.

Debugging delivery failures

The sandbox has its own webhook event feed — but it is a different pipeline with different event names than the production events on this page. See Sandbox for how to configure, list, and replay sandbox webhook events. For production issues, remember the delivery behaviour above: failed events are redelivered from the dead-letter queue with X-Webhook-Retry: true, and events that exhaust all attempts alert kurnl operations.