Guide
Webhooks
Signed accounting events in your own systems: endpoints, payload, signature, retries and the delivery log.
A webhook is an address in your system that DameDoklad calls when something happens: for example, when you issue an invoice or a payment comes in. That way you don’t have to keep asking the API what’s new.
You manage webhooks in Settings > Webhooks or through the API. Creating one takes a plan with webhooks; see pricing.
Create an endpoint
curl https://api.damedoklad.cz/v1/webhooks \
-H "Authorization: Bearer $DAMEDOKLAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/hooks/damedoklad", "events": ["invoice.issued", "received_invoice.*"] }'The response contains the signing secret, which starts with whsec_. You see it only once. If you lose it, replace it with POST /v1/webhooks/{id}/rotate-secret; the old secret stops working immediately.
In events, list event names, a prefix such as invoice.*, or * for everything. The address must be public: DameDoklad refuses private and local addresses, and checks again before each delivery.
What arrives
Each delivery is a POST with a JSON body:
{
"id": "evt_6f1c2a9b04d3e8f17a5c0b21",
"type": "invoice.issued",
"createdAt": "2026-10-30T09:12:44.120Z",
"workspaceId": "wsp_…",
"data": { "row": { "id": "inv_…", "number": "FV2026001", "status": "issued" }, "lines": [] }
}| Field | Meaning |
|---|---|
id |
The event ID. It stays the same when a delivery is retried, so use it to spot repeats. |
type |
The event name. |
createdAt |
When the event happened. |
workspaceId |
The company. |
data |
What the event says about the object. Its fields depend on the event, so load the current state through the API when you need more. |
These headers come with it:
| Header | Meaning |
|---|---|
X-DameDoklad-Event |
The event name. |
X-DameDoklad-Delivery |
The ID of this delivery, as in the delivery log. |
X-DameDoklad-Signature |
The signature, see below. |
User-Agent |
DameDoklad-Webhooks/1.0 |
Check the signature
The signature header looks like t=1793351564,v1=5f8a…. t is the send time in Unix seconds and v1 is a hexadecimal HMAC SHA-256 of the text {t}.{raw body}, keyed with your secret. Compare it over the raw bytes of the body, before parsing the JSON.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((part) => part.split('=')));
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const same = expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return same && fresh;
}import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(part.split("=", 1) for part in header.split(","))
signed = parts["t"].encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"]) and abs(time.time() - int(parts["t"])) < 300The five-minute window is our recommendation against replay. DameDoklad signs each attempt with a fresh t.
Retries
Any 2xx answer within 15 seconds counts as delivered. Anything else is retried, up to eight attempts in total:
| After attempt | Next attempt in |
|---|---|
| 1 | 1 minute |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 6 hours |
| 6 | 12 hours |
| 7 | 24 hours |
After the eighth failure the delivery stays in the log as failed, and you can send it again with POST /v1/webhook-deliveries/{id}/retry. See the log with GET /v1/webhooks/{id}/deliveries.
POST /v1/webhooks/{id}/test sends a signed webhook.test event straight away and returns the result. Test events are not retried.
Receiving events
- Respond quickly. Store the event and process it afterwards.
- Expect repeats. The same event can arrive more than once, so keep track of
id. - Don’t rely on order. When in doubt, load the object’s current state through the API.
- An event is sent only after its transaction commits, so a rolled-back invoice never announces itself.
Events
| Event | When it happens |
|---|---|
invoice.issued | An invoice was issued. |
invoice.storno | An invoice was reversed. |
invoice.payment_added | A payment was added to an invoice. |
invoice.payment_removed | A payment was removed from an invoice. |
received_invoice.recorded | A received invoice was recorded. |
received_invoice.storno | A received invoice was reversed. |
received_invoice.settled | A received invoice was settled. |
credit_note.issued | A credit note was issued. |
credit_note.storno | A credit note was reversed. |
credit_note.payment_added | A payment was added to a credit note. |
proforma.issued | A proforma was issued. |
proforma.payment_added | A payment was added to a proforma. |
delivery_note.issued | A delivery note was issued. |
member.invited | Someone was invited to the company. |
member.joined | Someone accepted an invitation. |
member.role_changed | A member's role changed. |
member.removed | A member was removed. |
company.updated | Company details changed. |
inbox.document_received | A document arrived in the To do inbox. |
inbox.document_finished | A document in the inbox was finished. |
Managing them through the API
You manage webhooks with these operations:
- Ask the shop to send a notification here when an order is created or changes, so an order arrives within a minute and not at the next 15-minute read. Turn it on with webhooksEnabled
POST /v1/shop-connections/{id}/register-webhooks - Send a failed delivery again
POST /v1/webhook-deliveries/{id}/retry - List the event types a webhook can subscribe to. Payloads are in the `webhooks` section of /openapi.json
GET /v1/webhook-events - List webhook endpoints
GET /v1/webhooks - Create a webhook endpoint. The signing secret is returned once.
POST /v1/webhooks - Get a webhook endpoint
GET /v1/webhooks/{id} - Update a webhook endpoint: URL, events, or pause it
PATCH /v1/webhooks/{id} - Delete a webhook endpoint and its delivery log
DELETE /v1/webhooks/{id} - List delivery attempts of a webhook endpoint, newest first
GET /v1/webhooks/{id}/deliveries - Replace the signing secret. The new secret is returned once; the old one stops working immediately.
POST /v1/webhooks/{id}/rotate-secret - Send a signed test event to this endpoint now and return the result. Test events are not retried.
POST /v1/webhooks/{id}/test