Preskočiť na obsah

Webhooks: events, delivery and retries

A webhook makes Dakoty call your server when something happens, so you do not have to poll. Dakoty sends an HTTPS POST with a JSON envelope for each event, signed so you can prove it came from Dakoty. This page covers the contract; to check signatures see Verifying signatures. People create webhooks by hand in the app (see the Slovak help article How to set up a webhook); tools such as Make subscribe through REST hooks.

Every delivery body is the same envelope. id is stable per event, repeated on every retry and equal to the webhook-id header, so use it for deduplication. test is true for sample and test deliveries. data is the resource exactly as the matching GET endpoint returns it.

{
"id": "evt_3f2c0a9e5b1d4c7e8a6f2b1c9d0e3a4b",
"type": "lead.created",
"timestamp": "2026-10-11T08:30:12Z",
"api_version": "2026-10-01",
"organization": { "id": "0b5f7c1e-3c52-4d0e-9a53-6f0c1f6a2b11", "slug": "acme" },
"test": false,
"data": { "id": "9a1d2c3e-4b5f-4a6b-8c7d-0e1f2a3b4c5d", "name": "Website enquiry", "status": "open", "value": 1200 }
}

Each request is a POST with Content-Type: application/json and the user agent Dakoty-Webhooks/1.0. Besides the signature headers it carries webhook-id, webhook-timestamp (Unix seconds, refreshed on every attempt), X-Dakoty-Event (the event type) and X-Dakoty-Attempt (1 for the first attempt). The signature headers webhook-signature and the legacy X-Dakoty-Signature are described in Verifying signatures.

Dakoty lists only events it really emits. A REST hook key needs the read scope of every event it subscribes to. The live list, with labels and descriptions, comes from GET /webhooks/events (scope webhooks:manage).

GroupEventsScope
Leadslead.created, lead.updated, lead.archived, lead.stage_changed, lead.won, lead.lostleads:read
Companies and contactscompany.created, company.updated, contact.created, contact.updatedcompanies:read
Clientsclient.created, client.updatedclients:read
Activitiesactivity.created, activity.completed, activity.cancelledactivities:read
Taskstask.created, task.moved, task.completed, task.overduetasks:read
Invoices and quotesinvoice.created, invoice.paid, invoice.overdue, quote.accepted, quote.rejectedinvoices:read
Bookingsbooking.created, booking.confirmed, booking.cancelled, booking.completed, booking.no_showbookings:read
Productsproduct.created, product.updatedproducts:read
Ordersorder.created, order.confirmed, order.paid, order.picking, order.packed, order.ready_for_pickup, order.shipped, order.delivered, order.cancelled, order.returnedorders:read
Order paymentsorder.payment_pending, order.payment_paid, order.payment_refunded, order.payment_failedorders:read

To build a mapping before any real event exists, fetch a static sample envelope with test: true. The response is an array with one envelope. It needs a key with webhooks:manage.

Terminal window
curl -sS https://api.dakoty.com/api/v1/public/webhooks/events/lead.created/sample \
-H "Authorization: Bearer $DAKOTY_API_KEY"

Respond with any 2xx status within 10 seconds. Anything else is a failed attempt: a non-2xx status, a timeout, a connection or TLS error, or a redirect (Dakoty does not follow redirects, so register the final URL). The target must be an https:// address on a public server; private and internal addresses are refused. Acknowledge first and process afterwards, because slow handlers time out and trigger retries.

A failed message is attempted up to nine times: the first attempt is immediate and the later ones follow at the delays below, which spreads the attempts over about two and a third days. If your server answers 429 or 503 with Retry-After, the next attempt waits at least that long, capped at 24 hours.

AttemptDelay after the previous attempt
1immediately
25 seconds
35 minutes
430 minutes
52 hours
65 hours
710 hours
814 hours
924 hours

Dakoty may deliver the same event more than once, for example when your server processed it but the response got lost. Retries can also make events arrive out of order. Make your handler idempotent: store the envelope id of every processed event and skip repeats, and use timestamp or a fresh GET of the resource when order matters. Each retry reuses the same webhook-id and body.

If a webhook has no successful delivery for 24 hours, Dakoty e-mails the people marked to be notified. After 72 hours without a success the webhook is disabled automatically, and new events are held for up to 30 days (at most 1,000 per webhook) instead of being lost. Webhooks owned by an app such as Make are not disabled but deleted, see REST hooks. The Slovak article Why a webhook fails shows the same states in the app.

Return 410 Gone from your endpoint to unsubscribe permanently. Dakoty stops delivering at once and does not retry: a manually created webhook is switched off with the reason “gone”, and a REST hook is deleted. Use it when the endpoint is retired for good, and use a 5xx status for a temporary outage you expect to recover from.