Webhooks přijímat signály v reálném čase
Nastavte HTTPS URL na svém Certyneo dashboardu a obdržíte POST podepsaný HMAC-SHA256 hned, jakmile se na vašich obálkách stane událost: podpis, odmítnutí, vypršení platnosti. 8 podporovaných událostí, exponenciální retry na 6 pokusů, 8 řádků šifrovacího ověření.
< 5s
Průměrná doba dodání po události
5x
Zkoušky v případě selhání (do ~ 9 hodin později)
HMAC-SHA256
Podpisový algoritmus pro každou žádost
Katalog událostí
8 následujících událostí pokrývá celý životní cyklus obalu Certyneo. Aktivujte ty, které vás zajímají v Nastavení → Webhooks, ostatní ignorujte předplatné je granulované na 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šechny signatáře podepsali. eIDAS zapečetěný PDF a auditní stopa jsou k dispozici prostřednictvím `proof_pdf_url` v nákladu. |
envelope.declined | Důvod pro odmítnutí (pokud je podepsaný signatářem) je uveden v `data.decline_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. Seznam chybějících signatářů je v `data.missing_signers`. |
recipient.signed | Použitelné pro postupné pracovní postupy: spouštět přechod na dalšího signatáře. |
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. |
Formát užitečného zatížení
Všechny události sdílejí stejný top-level JSON šablon: `event`, `envelope_id`, `occurred_at`, `data`. Obsah `data` se liší podle události (poly dokumentované podle události v odkazu API).
{
"event": "envelope.completed",
"envelope_id": "env_01HG3K8X9Y2N4P5Q6R7S8T9V0W",
"occurred_at": "2026-05-27T08:42:13.521Z",
"data": {
"title": "Contrat de prestation Acme Corp",
"status": "completed",
"created_at": "2026-05-24T14:12:00.000Z",
"completed_at": "2026-05-27T08:42:13.000Z",
"signers": [
{
"email": "client@acme.example",
"name": "Alex Client",
"signed_at": "2026-05-27T08:42:13.000Z",
"method": "eidas_aes",
"ip": "203.0.113.42"
}
],
"proof_pdf_url": "https://certyneo.com/api/envelopes/env_01HG.../proof.pdf"
}
}HMAC podpis je vypočítán na hrubém těle, jak byl odeslán.
Zkontrolujte podpis HMAC
Každá žádost je podepsána vaším webhookovým tajemstvím (je viditelná pouze jednou při vytvoření v přístrojovém panelu a pak šifrována na odpočinek).<timestamp>,v1=<hex_hmac>`. Vždy zkontrolujte podpis před zpracováním užitečného zatížení bez tohoto kroku může kdokoliv vytvořit událost a zavolat váš koncový bod.
Node.js / TypeScript
import crypto from "node:crypto";
export function verifyCertyneoSignature(
rawBody: string,
signatureHeader: string,
secret: string,
): boolean {
// Header format: t=<timestamp>,v1=<hex_hmac>
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=")),
);
const ts = parts.t;
const sig = parts.v1;
if (!ts || !sig) return false;
// Reject events older than 5 minutes (replay protection).
const age = Math.abs(Date.now() / 1000 - Number(ts));
if (age > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
// timingSafeEqual to mitigate timing attacks.
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(sig, "hex"),
);
}Python
import hashlib
import hmac
import time
def verify_certyneo_signature(
raw_body: bytes, signature_header: str, secret: str
) -> bool:
"""Verify a Certyneo webhook signature.
Header format: `t=<timestamp>,v1=<hex_hmac>`
Rejects events older than 5 minutes (replay protection).
"""
parts = dict(p.split("=") for p in signature_header.split(","))
ts = parts.get("t")
sig = parts.get("v1")
if not ts or not sig:
return False
if abs(time.time() - int(ts)) > 300:
return False
expected = hmac.new(
secret.encode("utf-8"),
f"{ts}.{raw_body.decode('utf-8')}".encode("utf-8"),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, sig)Č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áš koncový bod odpoví jinak než HTTP 2xx (timeout, 5xx, connection refused), budeme zvonit podle exponenciálního back-offu. Celkem 6 pokusů za ~9 hodin. Po 6 je událost označena jako `failed` ve vašem webhooku a bude zaslán e-mail s upozorněním.
| Zkus | Celková lhůta | Čas uplynulý |
|---|---|---|
| #1 | 0 | 0 |
| #2 | + 1 min | 1 min |
| #3 | + 5 min | 6 min |
| #4 | + 30 min | 36 min |
| #5 | + 2 h | 2h 36 |
| #6 | + 6 h | 8h 36 |
Pokud chcete manuálně odeslat neúspěšnou dodávku, webhookový panel nabízí tlačítko Re-deliver pro každou neúspěšnou dodávku k dispozici po dobu 7 dnů po konečném neúspěchu.
Testování bez zaslání skutečné obálky
Koncový bod `/v1/webhooks/test` přijímá URL a typ události a POSTe fiktivní payload podepsaný přesně jako skutečný.
# Trigger a test webhook to your endpoint
curl -X POST https://api.certyneo.com/v1/webhooks/test \
-H "Authorization: Bearer $CERTYNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"event": "envelope.completed", "url": "https://your.app/webhooks/certyneo"}'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í.
- Zpracovat každý `envelope_id` × `event` pouze jednou a uložit ID již viděné v bázi (zpětné přenosy mohou znovu poskytnout stejnou událost).
- Odpovězte na HTTP 200 nejvýše za 5 sekund a pak zpracujte asynchronně (v řadě).
- Logování hrubého těla + úplná debugovaná podpisnost HMAC ověření často selhává na BOM nebo neviditelném bílém prostoru.
- Zapojte dead-letter do čáry událostí `failed`, aby se ručně přenesl v případě incidentu na straně vaší služby.
- Tolerovat 5 minutové prodlení mezi `t=` a příjemnou dobou a dále, odmítnout, aby se blokovaly replay.
Pro další krok.
Připraveni na připojení systémů?
Vytvořte si bezplatný účet za 2 minuty nastavení webhooku je k dispozici již na Starterovém plánu (bezplatné, 5 obálek/měsíc).