Webhooks přijímat signály v reálném čase
Nakonfigurujte adresu URL HTTPS na vašem řídicím panelu Certyneo a přijímejte podepsaný POST HMAC-SHA256 pokaždé, když dojde k události na vašich obálkách: podpis, odmítnutí, expiraci. 11 podporovaných událostí, 5 pokusů o doručení s exponenciálním backoffem, kryptografické ověření na 8 řádcích kódu.
< 5s
Průměrná doba dodání po události
5x
Celkový počet pokusů o doručení, rozprostřených během přibližně 1 h 20
HMAC-SHA256
Podpisový algoritmus pro každou žádost
Katalog událostí
12 níže uvedených událostí pokrývá celý životní cyklus obálky Certyneo. Povolte ty, které vás zajímají, v Nastavení → Webhooky, ostatní ignorujte — odběr je granulární na úrovni událostí.
| Událost | Vypuknutí |
|---|---|
envelope.created | Vytvoří se obálka (v rámci UI, API nebo šablony) užitečná pro synchronizaci záznamu na straně CRM hned po jeho vytvoření. |
envelope.sent | Obálka je zaslána signatářům (první e-mail odeslán). |
envelope.completed | Všichni podepisující podepsali a zapečetěné eIDAS PDF je uloženo. Payload obsahuje signedDocumentUrl, předem podepsaný odkaz platný 7 dní; v opačném případě GET /v1/envelopes/id/signed-document a audit trail prostřednictvím GET /v1/envelopes/id/audit-trail. |
envelope.declined | Podepisující obálku odmítl. Adresa odmitatele je v `data.declinedBy` a důvod, pokud byl zadán, v `data.reason`. |
envelope.voided | Obálka byla zrušena před úplným podpisem. |
envelope.expired | Datum vypršení platnosti obálky uplynulo bez úplného podpisu. Dotazujte se na GET /v1/envelopes/id, abyste zjistili chybějící podepisuvatele. |
envelope.returned_to_sender | Podepisující vrátil obálku emitentovi ke korekci, aniž by ji odmítl. Důvod je v `data.reason` a autor vrácení v `data.returnedBy`. |
envelope.resubmitted | Emitent opravil a vrátil dříve vrácenou obálku. Znamená obnovení cyklu podpisu. |
recipient.signed | Jednotlivý podepisující podepsal (ale ne nutně všichni). Užitečné pro sledování pokroku a postupování na další fázi sekvenčního workflowu. Pozor: zapečetěné PDF v tomto stavu ještě neexistuje, ani pro posledního podpisuvatele — stahování spuštěné odsud vrací HTTP 409. Pro dokument přejděte na envelope.completed. |
recipient.viewed | Jeden z signatářů otevřel podpisový odkaz, aniž by to ještě podepsal, což je užitečné pro cílené obchodní revize. |
recipient.approved | Schvalovatel potvrdil obálku bez připojení podpisu (pracovní tok vnitřního ověření). Adresa je v `data.approvedBy`. |
recipient.bounced | Poštovní server příjemce trvale odmítl pozvánku nebo opakovaný pokus (neexistující poštovní schránka, mrtvá doména). Příjemce přejde do stavu BOUNCED a automatické opakované pokusy se zastavují. Adresa je v `data.recipientEmail`: opravte ji a obálku odešlete znovu. |
recipient.signed neznamená "dokument je dostupný"
recipient.signed je vydán pro KAŽDÉHO podpisuvatele v okamžiku, kdy dokončí svůj podpis — včetně posledního, dříve než je zapečetěné PDF sestaveno a uloženo. Stahování spuštěné z tohoto handleru tedy vždy obdrží HTTP 409 "Podepsaný dokument není dostupný, dokud není obálka COMPLETED". Nejde o chybu: je to "ještě není připraveno". Přihlaste se k odběru envelope.completed pro načtení dokumentu a udržujte recipient.signed pro sledování pokroku (kdo podepsal a kdy).
Formát užitečného zatížení
Všechna doručení sdílejí stejné schéma JSON na nejvyšší úrovni: `event`, `data` a `timestamp`. Název události se také opakuje v hlavičce `X-Certyneo-Event`, která umožňuje směrování ještě před analýzou těla. Obsah `data` se liší v závislosti na události, ale zůstává vždy plochý objekt s jednoduchými hodnotami — nikdy pole ani vnořený objekt. Zde je úplné doručení `envelope.completed`.
POST /webhooks/certyneo HTTP/1.1
Host: your.app
Content-Type: application/json
X-Certyneo-Event: envelope.completed
X-Certyneo-Delivery-Id: cm8f2h6a10004qr9k5p2wm3xt
X-Certyneo-Signature: 4f3d1c8b2a9e7f60d5c4b3a2918e7f6d5c4b3a2918e7f6d5c4b3a2918e7f6d5c{
"id": "cm8f2h6a10004qr9k5p2wm3xt",
"event": "envelope.completed",
"data": {
"envelopeId": "cm7x2k9p40001qz8h3f7bn2ld",
"sandbox": false,
"subject": "Contrat de prestation Acme Corp",
"status": "COMPLETED",
"signatureLevel": "ADVANCED",
"aesSignerCount": 2,
"completedAt": "2026-05-27T08:42:13.000Z",
"recipientCount": 2,
"recipients": [
{ "email": "alice@acme.com", "name": "Alice Martin", "role": "SIGNER", "status": "SIGNED" },
{ "email": "bob@acme.com", "name": "Bob Durand", "role": "SIGNER", "status": "SIGNED" }
],
"signedDocumentUrl": "https://storage.certyneo.com/signed/cm7x2k9p4.../contrat.pdf?X-Amz-Expires=604800&X-Amz-Signature=..."
},
"timestamp": "2026-05-27T08:42:13.521Z"
}signedDocumentUrl je předem podepsaný odkaz platný 7 dní od události: vyhýbá se druhému ověřenému volání pro načtení PDF. Chybí — a není null — pokud se předběžné podepisování nezdařilo, nebo na obálce QES dokončené bez uloženého dokumentu; poté přejděte na GET /v1/envelopes/id/signed-document, který zůstává zdrojem pravdy. Pozor také na ruční přehrávání z fronty mrtvých dopisů více než 7 dní po události: odkaz v payloadu je vypršel, koncový bod API nikoli.
HMAC podpis je vypočítán na hrubém těle, jak byl odeslán.
Zkontrolujte podpis HMAC
Každý požadavek je podepsán vaším webhooku secret (zobrazen pouze jednou při vytvoření předplatného). Podpis se přenáší v hlavičce `X-Certyneo-Signature` : jedná se o HMAC-SHA256 nezpracovaného těla požadavku, zakódovaného v hexadecimální podobě, bez předpony nebo časového razítka. VŽDY ověřte podpis před zpracováním payloadu — bez tohoto kroku si kdokoli může vymyslet událost a zavolat váš endpoint.
Node.js / TypeScript
import crypto from "node:crypto";
export function verifyCertyneoSignature(
rawBody: string,
signatureHeader: string,
secret: string,
): boolean {
// X-Certyneo-Signature = HMAC-SHA256(secret, rawBody), hex-encoded.
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const received = Buffer.from(signatureHeader, "hex");
const computed = Buffer.from(expected, "hex");
// timingSafeEqual throws when the two buffers differ in length —
// a malformed header must return false, not crash the handler.
if (received.length !== computed.length) return false;
// timingSafeEqual to mitigate timing attacks.
return crypto.timingSafeEqual(computed, received);
}Python
import hashlib
import hmac
def verify_certyneo_signature(
raw_body: bytes, signature_header: str, secret: str
) -> bool:
"""Verify a Certyneo webhook signature.
`X-Certyneo-Signature` holds the hex-encoded HMAC-SHA256 of the
raw request body, computed with your webhook secret.
"""
expected = hmac.new(
secret.encode("utf-8"),
raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature_header)Časté chyby
Nepoužívejte `===` nebo `==` k porovnání očekávané a přijaté podpisy. Používejte funkci time-safe (`crypto.timingSafeEqual` v Node, `hmac.compare_digest` v Pythonu).
Zkouška
Pokud váš endpoint trvá příliš dlouho na odpověď, odmítne připojení nebo vrátí 5xx (nebo 429), opakujeme podle exponenciálního zpětného postihu: 5 pokusů celkem, přibližně za 1 h 20. Po pátém pokusu se událost přesune do fronty dead-letter — zobrazitelná a přehrávatelná z vašeho panelu, ale již se automaticky neopakuje. Explicitní odmítnutí (401, 403, 404, 410, 422…) se naopak nikdy neopakuje: odpověď by se neměnila, událost jde přímo do fronty dead-letter.
| Zkus | Čas před pokusem | Uplynulý čas od události |
|---|---|---|
| #1 | 0 | 0 |
| #2 | + 1 min | 1 min |
| #3 | + 5 min | 6 min |
| #4 | + 15 min | 21 min |
| #5 | + 1 h | 1 h 21 |
Uvedené časy jsou minimální: kontrola opakování je řízena cronem, pokus tedy může vyjít mírně později než teoretický čas. Opuštěné události jsou uvedeny v sekci Webhooks → Selhání, s tlačítkem ruční přehrávky a bez omezení doby uchovávání. Endpoint, který selže pětkrát za sebou — nebo který vrátí 404 / 410 — je automaticky deaktivován a budete o tom informováni e-mailem: znovu jej aktivujte po opravě, čítač se vrátí na nulu (každé úspěšné doručení jej také vrátí na nulu).
Testování bez zaslání skutečné obálky
Nejprve vytvořte předplatné — odpověď obsahuje `secret` potřebný pro ověření HMAC, zobrazený pouze jednou. Z Nastavení → Webhooks potom tlačítko « Testovat » odešle podepsaný POST přesně jako skutečné doručení a zobrazí vám nezpracovanou odpověď vašeho serveru : je to nejrychlejší způsob, jak ověřit váš handler, místně přes ngrok nebo v rámci průběžné integrace.
# Create a subscription — "secret" is returned once, store it now.
curl -X POST https://api.certyneo.com/v1/webhooks \
-H "Authorization: Bearer $CERTYNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your.app/webhooks/certyneo",
"events": ["envelope.completed", "recipient.signed"]
}'URL musí být veřejně dostupná přes HTTPS : soukromá adresa nebo localhost je odmítnut ochranou SSRF při vytvoření předplatného.
Odběry jsou odděleny podle prostředí, stejně jako klíče: webhook vytvořený pomocí klíče sk_test_ přijímá pouze události z testovacích obálek, webhook vytvořený pomocí klíče sk_live_ pouze ty ze skutečných obálek. Testovací obálka se proto nikdy nedostane na vaši produkční URL a každá datová část označuje "sandbox" (true nebo false).
6 postupů, které je třeba dodržovat
- Zkontrolujte HMAC podpis PŘED každým čtením těla použít časově bezpečné srovnání.
- Deduplikujte na základě hlavičky `X-Certyneo-Delivery-Id` (stejné jako `id` v těle) uložením již viděných identifikátorů v databázi — přehrání může dodat událost, kterou jste již zpracovali, pokud se vaš 2xx ztratil, se stejným identifikátorem při každém pokusu.
- Odpovězte HTTP 2xx do 10 sekund maximálně, poté zpracovávejte asynchronně (fronta). Jestliže čas překročíte, doručení se přeruší a počítá se jako selhání.
- Logování hrubého těla + úplná debugovaná podpisnost HMAC ověření často selhává na BOM nebo neviditelném bílém prostoru.
- Sledujte stránku Webhooks → Selhání: budete informováni e-mailem, pokud bude váš endpoint deaktivován, ale ne událost za událostí — událost, která vyčerpá své pokusy, je na vás, abyste ji znovu přehráli.
- Filtrujte události při předplatném, nikoli ve vašem handleru, a zůstaňte pod limitem 5 předplatných na účet.
Není nutné hostovat koncový bod pro příjem těchto událostí: konektor nastaví předplatné za vás a spustí tok přímo na základě události obálky. Viz Certyneo pro Power Automate a Microsoft 365.
Pro další krok.
Připraveni na připojení systémů?
Webhooks a REST API jsou zahrnuty od plánu Standard. Vytvořte si účet, vygenerujte klíč `sk_test_` a připojte svůj endpoint v sandboxu dříve, než se přesunete do produkce.