Přeskočit na hlavní obsah

Webhooks API

Referenční popis webhooků: hlavičky, obálka a data, která aplikace skutečně posílá.

Jak webhook založit a ověřit jeho podpis, najdete na stránce Webhooks.

HTTP hlavičky​

Každý webhook je požadavek POST s těmito hlavičkami:

HlavičkaPopisPříklad
Content-TypeFormát tělaapplication/json
X-MujVykaz-EventNázev událostitime_entry.approved
X-MujVykaz-SignatureHMAC-SHA256 z celého těla požadavku, klíčem je tajný klíč webhookusha256=a1b2c3...
X-MujVykaz-DeliveryUnikátní ID doručení (UUID) — podle něj poznáte opakovaný pokus550e8400-e29b-41d4-a716-446655440000
User-AgentOdesílatelMujVykaz-Webhook/1.0

Obálka​

Tělo má vždy tři pole — event, timestamp a data:

{
"event": "time_entry.approved",
"timestamp": "2026-03-27T13:00:04.512Z",
"data": { "id": "3f2b9c1e-8a4d-4c1b-9f0e-2d7a6b5c4e3f" }
}
  • timestamp je čas odeslání v UTC.
  • Jeden požadavek nese jeden záznam. Hromadná akce (například schválení 20 výkazů) pošle 20 požadavků.
  • Všechna ID jsou UUID, názvy polí jsou camelCase, časy v UTC (ISO 8601) a částky v nejmenší jednotce měny — CZK v celých korunách, EUR a USD v centech.

Dostupné události​

Aplikace zná 24 událostí a všechny jdou zaškrtnout v dialogu Nový webhook.

SkupinaUdálosti
Výkazytime_entry.created, time_entry.updated, time_entry.deleted, time_entry.submitted, time_entry.approved, time_entry.invoiced
Fakturyinvoice.created, invoice.sent, invoice.paid, invoice.cancelled
Nákladyexpense.invoiced — jen uzavření nákladu. Vytvoření, úpravu, smazání ani schválení nákladu webhook neposílá.
Úkolytask.created, task.updated, task.completed, task.deleted
Týmuser.created, user.invited
Projektyproject.created, project.updated
Klienticlient.created, client.updated
Žádosti o přístup ke klientoviaccess_request.created, access_request.approved, access_request.rejected

Komentáře webhook nemají záměrně — jejich text je citlivá komunikace a posílat ho do cizího systému nechceme.

Formulář nabízí jen část událostí

Dialog Nový webhook (Nastavení → Webhooky) nabízí všech 24 událostí ve skupinách Výkazy (6), Faktury (4), Náklady (1), Úkoly (4), Tým (2), Projekty (2), Klienti (2) a Žádosti o přístup (3). Skupina Faktury se skryje, když má organizace vypnutou fakturaci — pak zbývá 20 událostí.

Do 8. 10. 2026 dialog nabízel jen 15 událostí; události úkolů, žádostí o přístup a uzavření výkazu a nákladu nešly zaškrtnout.

Data podle události​

Výkazy: created, updated, submitted, approved​

data je výkaz:

{
"event": "time_entry.approved",
"timestamp": "2026-03-27T13:00:04.512Z",
"data": {
"id": "3f2b9c1e-8a4d-4c1b-9f0e-2d7a6b5c4e3f",
"userId": "7c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"projectId": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"description": "Implementace API",
"startedAt": "2026-03-27T08:00:00.000Z",
"endedAt": null,
"durationMinutes": 150,
"hourlyRate": 1500,
"amount": 3750,
"currency": "CZK",
"status": "approved",
"isBillable": true,
"activityId": null,
"taskId": null,
"createdAt": "2026-03-27T10:31:12.000Z",
"updatedAt": "2026-03-27T13:00:04.000Z"
}
}
  • startedAt je v UTC: 08:00Z je 9:00 pražského času v zimě, 10:00 v létě.
  • Organizace bez schvalování pošle při odeslání výkazu rovnou time_entry.approved, ne time_entry.submitted.
  • Pole status ukazuje stav po akci, kterou událost hlásí: time_entry.submitted nese submitted, time_entry.approved nese approved.
  • Kdo výkaz schválil a kdy, webhook nenese.

Výkazy: deleted a invoiced​

time_entry.deleted nese jen ID smazaného výkazu:

{
"event": "time_entry.deleted",
"timestamp": "2026-03-27T15:12:40.201Z",
"data": { "id": "3f2b9c1e-8a4d-4c1b-9f0e-2d7a6b5c4e3f" }
}

time_entry.invoiced (výkaz uzavřený fakturou nebo uzávěrkou) nese výkaz se stejnými 16 poli jako ostatní události výkazů výše a status je už invoiced. Chodí při přidání výkazu do konceptu faktury a při uzavření v Uzávěrkách (i externí fakturou nebo obdobím paušálu). Vystavení nové faktury s výkazy (Nová faktura, faktura z klientského reportu) ji neposílá — chodí jen invoice.created.

Fakturu ani klienta data výkazu nenesou. Výkaz si podle id přečtete přes GET /api/v1/time-entries/{id}.

Co data výkazu nikdy nenesou

Data výkazu jsou u všech událostí time_entry.* jen pole z příkladu výše. Náklad člověka (odvozený ze mzdy), odkaz na událost v osobním kalendáři ani interní metadata výkazu na webhook neodcházejí.

Faktury​

invoice.created, invoice.sent (i když fakturu odešlete e-mailem přímo z aplikace), invoice.paid a invoice.cancelled nesou záznam faktury — mimo jiné id, number, clientId, totalAmount (základ daně), vatRate, vatAmount, totalWithVat, currency, status, issuedAt, dueAt, paidAt a cancelledAt.

Výjimka: invoice.created u faktury vystavené z klientského reportu nese jen souhrn:

{
"event": "invoice.created",
"timestamp": "2026-03-31T09:15:00.000Z",
"data": {
"invoiceId": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
"number": "FV-2026-042",
"clientId": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
"totalAmount": 46350,
"currency": "CZK",
"createdFromReportId": "8e7d6c5b-4a3f-4e2d-1c0b-9a8f7e6d5c4b"
}
}

Stejně krátký souhrn — jen invoiceId, number a status — nesou invoice.sent a invoice.paid při hromadné změně stavu (v přehledu faktur i přes PUT /api/v1/invoices/status).

ID faktury proto čtěte jako data.id ?? data.invoiceId. Organizace s vypnutou fakturací události invoice.* neposílá.

Faktura vystavená automaticky ze šablony opakované faktury a hromadné vystavení faktur invoice.created zatím neposílají.

Projekty a klienti​

Při založení nebo úpravě v aplikaci nese project.created / project.updated celý záznam projektu a client.created / client.updated celý záznam klienta (id, name a další pole).

Import z CSV a hromadné akce posílají většinou jen souhrn: projectId nebo clientId a name; hromadná změna stavu projektu přidá status, hromadná archivace klienta isActive. ID proto čtěte jako data.id ?? data.projectId, resp. data.id ?? data.clientId.

Úkoly​

task.created, task.updated a task.completed nesou úkol (id, projectId, title, status, assigneeId, dueDate a další pole). task.deleted nese jen id, title a projectId.

Tým​

user.created nese userId, name, email a role (některé cesty, třeba hromadné zakládání, jen userId a name). user.invited nese email a role pozvaného.

Náklady​

expense.invoiced nese náklad uzavřený fakturou nebo uzávěrkou, vždy stejných 12 polí se stavem po uzavření: id, projectId, invoiceId (u uzavření bez faktury v aplikaci null), status (invoiced), expenseDate, description, categoryId, billableAmount (částka pro klienta), currency, isBillable, createdAt a updatedAt. Nákupní cenu, interní poznámku ani odkaz na účtenku webhook nenese.

Chodí při přidání nákladu do konceptu faktury a při uzavření v Uzávěrkách. Vystavení nové faktury s náklady ji neposílá — chodí jen invoice.created.

Žádosti o přístup ke klientovi​

access_request.created nese requestId, clientId, requesterId a reason; access_request.approved nese requestId, clientId a requesterId; access_request.rejected navíc rejectReason.

HMAC ověření​

Podpis se počítá z celého těla požadavku (raw body) pomocí tajného klíče webhooku:

HMAC-SHA256(secret, raw_body) → hex digest → "sha256=" + digest

Vždy používejte timing-safe porovnání (viz Webhooks nastavení).

Retry politika​

PokusČekání
1. (původní)Okamžitě
2. pokus~2 sekundy
3. pokus~10 sekund

Opakuje se jen to, co se může samo spravit: chyba serveru (5xx), výpadek spojení nebo žádná odpověď do 10 sekund. Odpověď 3xx nebo 4xx se neopakuje — přesměrování nenásledujeme a chyba na straně klienta se opakováním nespraví. Po třetím neúspěšném pokusu se doručení vzdáme.

Auto-disable​

Pokud váš endpoint opakovaně selhává, webhook se po 10 po sobě jdoucích neúspěšných doručeních automaticky deaktivuje. Neúspěšné doručení je i odpověď 3xx nebo 4xx. Jediné úspěšné doručení počítadlo vynuluje.

Aplikace vás na to sama neupozorní — nepřijde e-mail ani zpráva v aplikaci. Počet chyb i to, jestli je webhook aktivní, uvidíte v Nastavení → Webhooky: deaktivovaný webhook má šedou tečku, červenou větu „Vypnuto automaticky po 10 chybách doručení v řadě — opravte adresu a zapněte ho znovu" a počet chyb (u odpovědi 3xx nebo 4xx i její HTTP kód). Postup: opravte endpoint, ověřte ho tlačítkem Test (musí vrátit 2xx) a klikněte na Aktivovat. Aktivace počítadlo chyb vynuluje, takže má webhook znovu 10 pokusů. Tlačítko Test počítadlo nemění. Když je pro vás doručování kritické, hlídejte si stav na své straně.

tip

Aplikace čeká na odpověď nejvýš 10 sekund. Odpovězte hned (200) a webhook zpracujte asynchronně — jinak se doručení počítá jako neúspěšné a zkusí se znovu.

Hromadné operace (bulk) a webhooky​

Při hromadném schválení nebo odeslání výkazů (bulk-status) jsou webhooky odesílány efektivně v dávkách s omezeným souběhem — ne synchronně jeden po druhém. To znamená:

  • Vaše aplikace může přijmout webhooky prakticky současně (paralelně, limit 10 souběžných)
  • Pořadí příjmu není garantováno (záleží na latenci sítě)
  • Každý výkaz v dávce generuje samostatný webhook event

Doporučujeme navrhovat endpoint jako idempotentní — bezpečné opakování zpracování stejného eventu (stejné X-MujVykaz-Delivery).