Přejít na hlavní obsah
Certyneo
Dokumenty vývojáře

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álostVypuknutí
envelope.createdVytvoří 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.sentObálka je zaslána signatářům (první e-mail odeslán).
envelope.completedVš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.declinedPodepisující obálku odmítl. Adresa odmitatele je v `data.declinedBy` a důvod, pokud byl zadán, v `data.reason`.
envelope.voidedObálka byla zrušena před úplným podpisem.
envelope.expiredDatum 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_senderPodepisují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.resubmittedEmitent opravil a vrátil dříve vrácenou obálku. Znamená obnovení cyklu podpisu.
recipient.signedJednotlivý 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.viewedJeden z signatářů otevřel podpisový odkaz, aniž by to ještě podepsal, což je užitečné pro cílené obchodní revize.
recipient.approvedSchvalovatel potvrdil obálku bez připojení podpisu (pracovní tok vnitřního ověření). Adresa je v `data.approvedBy`.
recipient.bouncedPoš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 pokusemUplynulý čas od události
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 15 min21 min
#5+ 1 h1 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.