Webhooky
Webhooky vám doručia udalosti (faktúry, platby, sklad, Peppol doklady) na váš HTTPS endpoint. Každé doručenie je podpísané HMAC‑om a opakuje sa s exponenciálnym backoffom, kým ho nepotvrdíte HTTP 2xx. Firma s vlastným API kľúčom si endpoint registruje cez API (prvá sekcia), partner v portáli (ďalšie sekcie). Podpis, hlavičky a opakovania sú v oboch prípadoch rovnaké.
Webhooky pre firmy (vlastný API kľúč)
Endpoint zaregistrujete API kľúčom s rozsahom settings:update. Funguje od plánu Pro. Odpoveď obsahuje podpisový secret iba raz. Najviac 20 endpointov na firmu, URL musí byť https://. Rovnaké endpointy vidíte v eDoklade v Nastavenia → Webhooky. Okamžité spúšťače v Make a n8n si endpoint vytvárajú a mažú samy.
curl -X POST https://api.efaktura.sk/v1/agent/webhooks \
-H "X-API-Key: efk_live_…" \
-H "X-Organization-Id: <uuid>" \
-H "Content-Type: application/json" \
-d '{"url":"https://erp.example.sk/efaktura","events":["peppol.document.received","invoice.paid"]}'| Udalosť | Kedy |
|---|---|
invoice.created | Vznikol doklad — v appke, cez API, importom, opakovanou faktúrou, z e-shopu. |
invoice.updated | Zmenil sa stav, číslo, suma alebo splatnosť. |
invoice.deleted | Doklad bol zmazaný. |
invoice.sent | Stav faktúry je odoslaná; invoice.viewed = odberateľ otvoril odkaz. |
invoice.paid | Faktúra je uhradená (banka aj ručne). |
invoice.overdue | Faktúra je po splatnosti. |
payment.received | K faktúre sa spárovala platba. |
reminder.sent | Odišla upomienka. |
peppol.document.* | sent / delivered / failed / received — ako pri partneroch nižšie. |
stock.* | level_changed / document_confirmed / low — sklad. |
Udalosti faktúr nesú v data polia invoiceId, invoiceNumber, type, status, previousStatus, total (string "149.99"), currency, issueDate, dueDate, paidDate, customerName a pdfUrl. Históriu doručení vráti GET /v1/agent/webhooks/{id}/deliveries, opätovné odoslanie POST …/deliveries/{deliveryId}/redeliver, testovaciu udalosť POST /v1/agent/webhooks/{id}/test. Doručenia uchovávame 90 dní. Kompletný zoznam endpointov je v API referencii (sekcia Webhooks).
Partneri: registrácia endpointu
Endpoint zaregistrujete v portáli v sekcii Webhooky alebo cez POST /v1/partner/webhooks — to je endpoint portálu overený vaším prihlásením (session cookie), nie hlavičkou X-API-Key; s API kľúčom vráti 401 UNAUTHORIZED. Pri vytvorení dostanete podpisový secret iba raz. Uložte si ho. Jeden partner môže mať najviac 20 endpointov; URL musí byť https://. Endpoint štandardne dostáva udalosti za všetky organizácie partnera; voliteľným orgId pri vytvorení ho obmedzíte na jedinú organizáciu.
Partneri: typy udalostí
| Event | Kedy |
|---|---|
peppol.document.sent | Doklad prijatý sieťou (AS4 receipt). |
peppol.document.delivered | Doklad doručený príjemcovi (MLS/doručenka). |
peppol.document.failed | Odoslanie zlyhalo (finálne). |
peppol.document.received | Prišiel vám príchodzí doklad cez Peppol. |
peppol.document.fs_report_accepted | Finančná správa prijala hlásenie k odoslanému alebo prijatému dokladu. |
peppol.document.fs_report_rejected | Finančná správa hlásenie zamietla; dôvody sú v data.issues. |
participant.activated | Organizácia je zaregistrovaná v SMP a prijíma doklady (po potvrdení FS mandátu alebo po POST /v1/agent/peppol/enroll). |
participant.failed | Registrácia účastníka zlyhala (neplatný FS token, chyba SMP, kolízia s inou firmou, deaktivovaná organizácia). |
participant.deactivated | Organizácia bola odregistrovaná zo SMP (POST /v1/agent/organizations/{id}/peppol/deactivate). |
usage.limit_exceeded | Organizácia prekročila zahrnutý mesačný objem transakcií. |
Payload udalostí participant.* nesie data.participantId (0245:DIČ, v sandboxe 9915:DIČ), data.scheme, data.mode (live / test), data.source (partner_claim / api_enroll / api_deactivate) a časovú pečiatku activatedAt / failedAt / deactivatedAt; participant.failed navyše data.code a data.message.
Payload udalostí peppol.document.fs_report_* nesie data.direction (sent / received), data.invoiceId alebo data.receivedDocumentId, data.originalDocumentId, data.state, data.issues a data.decidedAt. Aktuálny stav hlásenia je aj v poli fs_report v GET /v1/agent/peppol/status/{invoiceId} a GET /v1/agent/peppol/received/{id}.
peppol.document.status_changed je v katalógu, ale zatiaľ sa neemituje. Zmeny stavu prenosu pokrývajú eventy sent / delivered / failed vyššie.Formát payloadu
Telo je JSON s obálkou event / timestamp (ISO 8601) / data. Pole data.orgId je vždy prítomné a identifikuje klientsku organizáciu. Pri test sendoch nesie data.mode = "test".
{
"event": "peppol.document.delivered",
"timestamp": "2026-06-12T19:48:01.000Z",
"data": {
"invoiceId": "b1f0…",
"invoiceNumber": "2026001",
"documentType": "invoice",
"mode": "test",
"state": "DELIVERED",
"messageId": "…",
"transactionId": "…",
"orgId": "0a2c…"
}
}| Event | Polia v data (okrem orgId) |
|---|---|
sent | invoiceId, invoiceNumber, documentType, mode, state, messageId, transactionId |
delivered | invoiceId, invoiceNumber, documentType, mode, state, messageId, transactionId |
failed | invoiceId, invoiceNumber, documentType, error |
received | senderName, senderParticipantId, documentNumber, documentType, total, currency |
Hlavičky
| Hlavička | Popis |
|---|---|
X-Webhook-Signature | Podpis vo formáte t=<unix>,v1=<hex> (Stripe model). |
X-Webhook-Event | Názov udalosti. |
X-Webhook-Id | UUID doručenia (idempotenčný kľúč). |
X-Webhook-Timestamp | Unix čas podpisu (sekundy). |
Overenie podpisu
Podpisuje sa reťazec <timestamp>.<raw body> pomocou HMAC‑SHA256 a vášho secretu, výsledok je lowercase hex v segmente v1=. Počas rotácie secretu môže hlavička obsahovať viac segmentov v1= (starý + nový): stačí, ak sa zhoduje ktorýkoľvek. Porovnávajte v časovo‑konštantnom čase a odmietnite príliš staré timestamp (ochrana proti replayu).
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
header.split(",").map((kv) => kv.split("="))
);
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
// header may carry several v1=… segments during secret rotation
return header
.split(",")
.filter((kv) => kv.startsWith("v1="))
.some((kv) => {
const got = kv.slice(3);
return (
got.length === expected.length &&
timingSafeEqual(Buffer.from(got), Buffer.from(expected))
);
});
}Doručovanie a opakovania
Doručenie je durabilné (fronta + perzistovaná história). Endpoint má odpovedať HTTP 2xx do niekoľkých sekúnd; spracovanie spravte asynchrónne. Pri zlyhaní (ne‑2xx alebo timeout) sa doručenie opakuje 10× počas približne 2,7 dňa: spolu 11 pokusov, takže endpoint, ktorý cez víkend nebeží, dostane všetky udalosti.
| Pokus | Čakanie po predošlom zlyhaní |
|---|---|
| 2. | 5 s |
| 3. | 1 min |
| 4. | 5 min |
| 5. | 30 min |
| 6. | 2 h |
| 7. | 5 h |
| 8. | 10 h |
| 9. | 10 h |
| 10. | 14 h |
| 11. (posledný) | 24 h |
Po poslednom neúspešnom pokuse sa doručenie označí ako zlyhané; z portálu ho stále viete znovu odoslať (replay).
| Mechanizmus | Správanie |
|---|---|
| Opakovania | Iniciálny pokus + 10 opakovaní počas ~2,7 dňa (tabuľka vyššie). |
| Idempotencia | Deduplikujte podľa X-Webhook-Id: replay pošle rovnaké ID. |
| Auto‑vypnutie | Po 15 po sebe zlyhaných doručeniach sa endpoint deaktivuje. |
| Rotácia secretu | Starý secret overuje ďalších 24 h (dvojitý v1= podpis). |
Schránka udalostí (pull)
Každá udalosť sa zapíše aj do schránky udalostí, a to aj keď nemáte zaregistrovaný žiadny endpoint. Ak webhook prijať nemôžete (firewall) alebo výpadok trval dlhšie ako opakovania, stiahnite si zmeškané udalosti cez GET /v1/agent/events partnerským kľúčom. Telo v poli payload je presne to, čo by poslal webhook.
curl "https://api.efaktura.sk/v1/agent/events?after=1841&limit=100" \
-H "X-API-Key: efk_pk_live_…"| Pravidlo | Správanie |
|---|---|
| Kurzor | Pošlite after=<next_after> z predošlej odpovede, kým has_more je true. |
| Poradie | Od najstaršej udalosti; id je rastúce číslo (string). |
| Deduplikácia | Podľa event_id, ak je vyplnené; inak sa udalosť zapíše pri každom výskyte. |
| Uchovanie | 90 dní. |
| Rozsah | Všetky vaše organizácie; každá udalosť nesie org_id. |
Testovanie
V portáli viete poslať testovací event na endpoint, pozrieť si históriu doručení (payload + odpoveď) a znovu‑odoslať (replay) ktorékoľvek minulé doručenie. V sandboxe (test kľúč) fire‑uje reálny peppol.document.delivered s data.mode = "test" po doručení medzi zaregistrovanými test účastníkmi.