Prejsť na hlavný obsah
Certyneo
Dokumentácia vývojára

Webhooks prijímajte podpísané udalosti v reálnom čase

Nastavte HTTPS URL na svojom palubnom paneli Certyneo a dostanete POST podpísaný HMAC-SHA256 hneď, ako sa na vašich obálkach stane niečo: podpis, odmietnutie, vypršanie platnosti. 8 podporovaných udalostí, exponenciálny retry na 6 pokusov, šifrovanie 8 riadkov kódu.

< 5s

Priemerné obdobie dodania po udalosti

5x

Skúšky v prípade neúspechu (do ~ 9 h neskôr)

HMAC-SHA256

Algorytmus podpisu každej žiadosti

Katalog udalostí

8 nasledujúcich udalostí pokrýva celý životný cyklus obálky Certyneo. Aktivujte tie, ktoré vás zaujímajú v Nastavenia → Webhooks, ignorujte ostatné predplatné je granulované na udalosť.

PodujatieVypúšťanie
envelope.createdVytvorí sa obal (v rámci UI, API alebo šablóny) užitočný na synchronizáciu záznamu na strane CRM hneď po jeho vytvorení.
envelope.sentObálka je zaslaná signatárom (prvý odoslaný e-mail).
envelope.completedVšetky podpisy podpísané. eIDAS pečate PDF a audit trail sú k dispozícii prostredníctvom `proof_pdf_url` v nákladu.
envelope.declinedDôvod odmietnutia (ak ho podpísaný poskytne) je v `data.decline_reason`.
envelope.voidedObálka bola zrušená odosielateľom pred úplnou podpisom.
envelope.expiredDátum platnosti obálky uplynul bez úplného podpisu. Zoznam chýbajúcich signatárov je v `data.missing_signers`.
recipient.signedPoužité pre postupné pracovné postupy: spustiť prechod na ďalšieho signatára.
recipient.viewedJeden signatár otvoril podpisové prepojenie bez toho, aby to ešte podpísal, čo je užitočné pre cielené obchodné reklamu.

Formát užitočného nákladu

Všetky udalosti zdieľajú rovnakú JSON top-level šablónu: `event`, `envelope_id`, `occurred_at`, `data`. Obsah `data` sa líši podľa udalosti (oblasti dokumentované podľa udalosti v referencii 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"
  }
}

Pôžitný náklad je kódovaný v UTF-8, bez BOM. HMAC podpis sa počíta na hrubom tele, ako je odoslaný nezmenujte medzery, re-parsing JSON často mení poradie kľúčov a porušuje overenie.

Overte podpis HMAC

Každá žiadosť je podpísaná s vaším webhookovým tajomstvom (viditeľný iba raz pri vytváraní v paneli a potom šifrovaný na odpočinok).<timestamp>,v1=<hex_hmac>Vždy skontrolujte podpis pred spracovaním užitočného nákladu bez tohto kroku môže ktokoľvek vytvoriť udalosť a zavolať 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

Použite funkciu time-safe (`crypto.timingSafeEqual` v Node, `hmac.compare_digest` v Pythone), inak časový rozdiel medzi dvoma podpismi postupne odhalí tajomstvo pacientovi (timing attack).

Politika retry

Ak váš koncový bod odpovedá inak ako HTTP 2xx (timeout, 5xx, connection refused), budeme zazvonovať podľa exponenciálneho back-offu. Celkovo 6 pokusov za ~9 hodín. Po 6 sa udalosť označí ako `failed` vo vašom webhooku a bude odoslaný upozorňujúci e-mail.

SkúšanieZvyšná lehotaČas uplynutý
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

Ak chcete manuálne odovzdať zlyhanú dodávku, webhookový dashboard ponúka tlačidlo Re-deliver na každú zlyhanú dodávku k dispozícii 7 dní po definitívnom zlyhaní.

Testovanie bez zaslania skutočnej obálky

Koncový bod `/v1/webhooks/test` prijíma URL a typ udalosti a POSTe fiktívnu payload podpísanú presne ako skutoč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 postupov, ktoré je potrebné dodržiavať

  • Zkontrolujte HMAC podpis PREČO čítať body použite časovo bezpečné porovnanie.
  • Spracúvať každý `envelope_id` × `event` iba raz a ukladať ID, ktoré už boli v báze videné (zobrazovanie môže znovu poskytovať rovnakú udalosť).
  • Odpovedať na HTTP 200 v maximálne 5 sekundách a potom spracovať asynchrónne (v rade).
  • Logovať hrubé telo + úplný podpis v debugovaní HMAC overovanie často zlyhá na neviditeľnej BOM alebo bielom priestore.
  • Pripojte dead-letter na `failed` udalosti, aby ste ho manuálne prešli v prípade incidentu na strane vašej služby.
  • Dovoľte, aby sa 5 minút presunulo od `t=` do času prijatia a ďalej, odmietnite, aby sa blokovali relády.

Na ďalší krok

Ste pripravení zapojiť vaše systémy?

Vytvorte si bezplatné konto za 2 minúty konfigurácia webhooku je dostupná už v Starter pláne (zadarmo, 5 obálok/mesiac).