Zaslat dokumentace
Zaslat API v2

Webhooky – změny stavu zásilek

Webhook je adresa URL ve vašem systému, na kterou Zaslat posílá HTTP požadavek pokaždé, když se změní stav některé z vašich zásilek. Nemusíte se tak na stav zásilek opakovaně dotazovat přes shipments/tracking; informace přijde sama krátce poté, co zpracujeme trackingová data dopravce.

Webhooky spravujete přes API v2 v sekci Webhooks API Reference. Všechny požadavky posíláte s hlavičkou x-apikey.

Registrace webhooku

Endpoint webhook/create zaregistruje adresu, na kterou chcete události dostávat:

TerminalCode
curl -X POST https://test.zaslat.cz/api/v2/public/webhook/create \ -H "x-apikey: VAS_API_KLIC" \ -H "Content-Type: application/json" \ -d '{"url": "https://www.example.com/webhooks/zaslat"}'

Odpověď obsahuje identifikátor webhooku a podpisový klíč v poli secret:

JSONCode
{ "id": 42, "url": "https://www.example.com/webhooks/zaslat", "event": "shipment.status_changed", "active": true, "createdAt": "2026-10-07T14:03:11+02:00", "failureCount": 0, "lastSuccessAt": null, "lastFailureAt": null, "disabledAt": null, "secret": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }

Podpisový klíč se vrací jen jednou

Pole secret je součástí pouze odpovědi na webhook/create. Uložte si jej; v detailu ani v seznamu webhooků se už nezobrazí. Pokud klíč ztratíte, webhook smažte a zaregistrujte znovu. Vlastní klíč (16 až 128 znaků) můžete poslat v poli secret požadavku.

Pravidla pro adresu:

  • Pouze https://, bez přihlašovacích údajů v URL.
  • Doménové jméno se musí přeložit na veřejnou IP adresu. Privátní, lokální a rezervované adresy (například 10.x, 192.168.x, 127.0.0.1, 169.254.x) jsou odmítnuty se stavem 400.
  • Na jeden účet lze zaregistrovat nejvýše 10 webhooků. Stejnou adresu lze pro jednu událost zaregistrovat jen jednou; opakovaný pokus vrátí 409.

Správa webhooků

EndpointPopis
GET webhook/listSeznam všech webhooků účtu bez podpisových klíčů.
GET webhook/detail/{id}Detail jednoho webhooku.
PATCH webhook/update/{id}Změna adresy (url) nebo pozastavení a obnovení (active). Posílejte jen pole, která měníte.
DELETE webhook/delete/{id}Trvalé smazání webhooku; události čekající na doručení se zahodí.
GET webhook/deliveries/{id}Posledních 50 pokusů o doručení se stavovým kódem a popisem chyby.
POST webhook/test/{id}Odeslání testovací události (viz níže).

Pozastavený webhook ("active": false) si zachová nastavení, ale události na něj neposíláme a po obnovení je zpětně nedoručujeme.

Událost shipment.status_changed

Událost vzniká při změně souhrnného stavu zásilky v Zaslat (například INTRANSIT → ONDELIVERY → DELIVERED), nikoli při každém záznamu trackingu dopravce. Slovník stavů vychází ze sledování zásilek (CREATED, EXPORTED, ONPICKUP, INTRANSIT, ONDELIVERY, DELIVERED, CANCELLED; význam popisuje stránka Sledování zásilek a stavy) a navíc rozlišuje dva stavy:

  • PARCELSHOP – zásilka je uložena na výdejním místě nebo v boxu a čeká na vyzvednutí příjemcem.
  • DONE_AUTO – zásilku jsme uzavřeli automaticky, protože dopravce 90 dní neposlal žádný nový stav.

Na vaši adresu přijde požadavek POST s tělem application/json:

JSONCode
{ "event": "shipment.status_changed", "eventId": "7f2a0b9c-4d3e-4f61-9a8b-2c1d0e5f6a7b", "version": 1, "occurredAt": "2026-10-07T14:03:11+02:00", "test": false, "data": { "id": 4815162, "trackingNumber": "IZ3F9A2C71B4", "carrierNumber": "13606100012345", "referenceNumber": "ORDER-2026-1042", "carrier": { "code": "dpd", "name": "DPD" }, "status": "DELIVERED", "trackingUrl": "https://www.zaslat.cz/cs/sledovani-zasilek/IZ3F9A2C71B4", "lastTrackingEvent": { "date": "2026-10-07 13:58:00", "status": "DELIVERED", "name": "Zásilka byla doručena příjemci", "location": "Praha 5" } } }
PoleVýznam
eventTyp události.
eventIdJedinečný identifikátor události. Opakované doručení téže události má stejné eventId; použijte jej k odfiltrování duplicit.
versionVerze schématu obálky. Zvýší se jen při nekompatibilní změně pole data.
occurredAtČas změny stavu (ISO 8601 včetně časového pásma).
testtrue u událostí z testovacího endpointu, false u skutečných zásilek.
data.id, data.trackingNumberIdentifikátory zásilky v Zaslat.
data.carrierNumberČíslo balíku u dopravce; null, dokud zásilka není exportována k dopravci.
data.referenceNumberVaše referenční číslo zadané při vytvoření zásilky.
data.carrierKód a název dopravce; null, pokud dopravce ještě není přiřazen.
data.statusNový stav zásilky.
data.trackingUrlVeřejná stránka sledování zásilky.
data.lastTrackingEventPoslední záznam trackingu od dopravce; null, pokud zatím žádný není.

Událost obsahuje pouze identifikátory a nový stav. Podrobnosti o zásilce včetně adres si v případě potřeby načtěte přes API.

Ověření podpisu

Každý požadavek nese hlavičku X-Zaslat-Signature ve tvaru sha256=<hex>, kde <hex> je HMAC-SHA256 nezměněného těla požadavku vypočtený podpisovým klíčem webhooku. Na své straně podpis spočítejte z přesně přijatých bajtů těla a porovnejte jej s hlavičkou porovnáním v konstantním čase. Požadavek s neplatným podpisem odmítněte.

Hlavičky X-Zaslat-Event a X-Zaslat-Event-Id opakují hodnoty event a eventId z těla a hodí se pro směrování požadavku ještě před jeho zpracováním. API klíč se na webhook nikdy neposílá.

PHPCode
$body = file_get_contents('php://input'); $expected = 'sha256=' . hash_hmac('sha256', $body, $secret); if (!hash_equals($expected, $_SERVER['HTTP_X_ZASLAT_SIGNATURE'] ?? '')) { http_response_code(401); exit; }
JavascriptCode
const crypto = require("crypto"); const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex"); const received = req.headers["x-zaslat-signature"] ?? ""; if (received.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) { res.status(401).end(); return; }

Podpis počítejte z původního těla požadavku, nikoli z JSON, který jste znovu serializovali. Změna pořadí klíčů nebo mezer podpis zneplatní.

Doručování a opakování

Váš endpoint musí odpovědět stavem 2xx do 10 sekund. Přesměrování nenásledujeme a počítá se jako selhání.

OdpověďCo se stane
2xxUdálost je doručena.
3xx, 5xx, 408, 429, vypršení času nebo chyba spojeníDoručení opakujeme s rostoucím odstupem 1, 5, 15 a 30 minut, poté 1, 2, 4, 8, 12 a 24 hodin od předchozího pokusu (celkem 10 opakování), vždy se stejným eventId.
Ostatní 4xxUdálost považujeme za trvale odmítnutou a dále ji neopakujeme.

Pokud všechny pokusy o doručení selhávají nepřetržitě 24 hodin, webhook automaticky pozastavíme: pole active se změní na false, čas pozastavení najdete v poli disabledAt a na notifikační e-mail účtu odešleme upozornění. Události vzniklé během pozastavení se neposílají ani zpětně nedoručují. Po opravě endpointu webhook obnovíte požadavkem PATCH webhook/update/{id} s tělem {"active": true}; tím se vynuluje i čítač failureCount.

Stav doručování sledujte v polích failureCount, lastSuccessAt a lastFailureAt detailu webhooku a v endpointu webhook/deliveries/{id}, který vrací posledních 50 pokusů včetně stavového kódu a popisu chyby. Záznamy starší než 30 dní se mažou.

Testovací událost

Endpoint POST webhook/test/{id} ihned odešle na adresu webhooku jednu událost shipment.status_changed se smyšlenými daty zásilky a "test": true, podepsanou stejně jako skutečná událost, a vrátí, jak váš endpoint odpověděl:

TerminalCode
curl -X POST https://test.zaslat.cz/api/v2/public/webhook/test/42 \ -H "x-apikey: VAS_API_KLIC"
JSONCode
{ "eventId": "c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "delivered": true, "statusCode": 200, "error": null }

Testovací událost se odešle i na pozastavený webhook a nikdy neovlivní čítače selhání ani automatické pozastavení; v přehledu doručení je označena "test": true. Neúspěšný test vrátí také stav 200, výsledek poznáte z polí delivered, statusCode a error. Endpoint je omezen na 10 požadavků za 60 sekund na účet; po překročení limitu dostanete 429.

Last modified on