Webhooks — ontvang handtekeningsgebeurtenissen in realtime
Configureer een HTTPS-URL in uw Certyneo-dashboard en ontvang een HMAC-SHA256-ondertekende POST zodra een gebeurtenis optreedt op uw enveloppen: ondertekening, weigering, verlopen. 8 ondersteunde gebeurtenissen, exponentiële retry over 6 pogingen, cryptografische verificatie in 8 regels code.
< 5s
Gemiddelde bezorgingstijd na de gebeurtenis
5x
Pogingen bij mislukking (tot ongeveer 9 uur later)
HMAC-SHA256
Handtekeningsalgoritme van elke aanvraag
Catalogus van gebeurtenissen
De 8 onderstaande gebeurtenissen dekken de volledige levenscyclus van een Certyneo-envelop. Activeer degenen die u interesseren in Instellingen → Webhooks, negeer de rest — het abonnement is granulair per gebeurtenis.
| Gebeurtenis | Activering |
|---|---|
envelope.created | Een envelop wordt aangemaakt (via UI, API of template) — nuttig om een CRM-record direct na aanmaking te synchroniseren. |
envelope.sent | De envelop wordt naar de ondertekenars verzonden (eerste e-mail verzonden). Markeert het begin van de actieve ondertekeningsmyclus. |
envelope.completed | Alle ondertekenars hebben ondertekend. De verzegelde eIDAS-PDF en het audittrail zijn beschikbaar via `proof_pdf_url` in de payload. |
envelope.declined | Een ondertekenaar heeft de envelop afgewezen. De afwijzingsreden (indien verstrekt door de ondertekenaar) staat in `data.decline_reason`. |
envelope.voided | De envelop is door de afzender geannuleerd voordat de ondertekening voltooid was. Verschilt van `expired` (mens vs timeout). |
envelope.expired | De vervaldatum van de envelop is verstreken zonder volledige ondertekening. De lijst met ontbrekende ondertekenars staat in `data.missing_signers`. |
recipient.signed | Een individuele ondertekenaar heeft ondertekend (maar niet noodzakelijk allemaal). Nuttig voor opeenvolgende workflows: activeer de overgang naar de volgende ondertekenaar. |
recipient.viewed | Een ondertekenaar heeft de ondertekeningslink geopend zonder nog te hebben ondertekend. Nuttig voor gerichte vervolgacties. |
Payload-indeling
Alle gebeurtenissen delen hetzelfde top-level JSON-schema: `event`, `envelope_id`, `occurred_at`, `data`. De inhoud van `data` varieert per gebeurtenis (velden gedocumenteerd per gebeurtenis in de API-referentie). Hier volgt een volledig voorbeeld van `envelope.completed`.
{
"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"
}
}De payload is UTF-8 gecodeerd, zonder BOM. De HMAC-handtekening wordt berekend op het onbewerkte body zoals verzonden — wijzig geen spaties, herparsen van JSON wijzigt vaak de volgorde van sleutels en verbreekt de verificatie.
HMAC-handtekening verifiëren
Elke aanvraag wordt ondertekend met uw webhook-geheim (eenmalig zichtbaar bij aanmaking in het dashboard, daarna versleuteld in rust). De handtekening wordt doorgegeven in de `Certyneo-Signature`-header in het formaat `t=<timestamp>,v1=<hex_hmac>`. Verifieer ALTIJD de handtekening voordat u de payload verwerkt — zonder deze stap kan iedereen een gebeurtenis vervalsen en uw eindpunt aanroepen.
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)Veel voorkomende fout
Gebruik GEEN `===` of `==` om de verwachte handtekening met de ontvangen handtekening te vergelijken. Gebruik een timing-safe functie (`crypto.timingSafeEqual` in Node, `hmac.compare_digest` in Python). Zonder dit onthult het tijdsverschil in vergelijking tussen twee handtekeningen geleidelijk het geheim aan een geduldig aanvaller (timing attack).
Beleid voor opnieuw proberen
Als uw eindpunt iets anders antwoordt dan HTTP 2xx (timeout, 5xx, verbinding geweigerd), doen we een poging opnieuw volgens exponentieel backoff. In totaal 6 pogingen gedurende ~9 uur. Na de 6e poging wordt de gebeurtenis als `failed` gemarkeerd in uw webhook-dashboard en wordt een waarschuwings-e-mail verzonden.
| Poging | Cumulatieve tijd | Verstreken tijd |
|---|---|---|
| #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 |
Als u een gemiste gebeurtenis handmatig opnieuw wilt verzenden, biedt het webhook-dashboard een knop « Re-deliver » voor elke mislukte aflevering — beschikbaar gedurende 7 dagen na de definitieve fout.
Testen zonder een echte envelop te verzenden
Het eindpunt `/v1/webhooks/test` accepteert een URL en een gebeurtenistype, en POSTet een fictieve payload die exact zoals een echte is ondertekend. Ideaal om uw handler in continue integratie of tijdens lokale ontwikkeling te valideren (met ngrok of gelijkwaardig).
# 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 praktijken om na te leven
- Controleer de HMAC-handtekening VOOR elke body-leezing — gebruik een timing-safe vergelijking.
- Verwerk elke `envelope_id` × `event` slechts eenmaal door reeds geziene ID's in de database op te slaan (retries kunnen dezelfde gebeurtenis opnieuw afleveren).
- Antwoord HTTP 200 binnen maximaal 5 seconden, vervolgens verwerken asynchroon (wachtrij). Anders bent u in timeout en wordt u als retry geteld.
- Log de onbewerkte body + volledige handtekening in debug — HMAC-verificatie mislukt vaak vanwege een BOM of onzichtbare witruimte.
- Sluit een dead-letter queue aan op `failed` gebeurtenissen voor handmatige reaflevering in geval van incident aan uw servicezijde.
- Tolereer een verschil van 5 minuten tussen `t=` en de ontvangsttijd — afwijzen voorbij die limiet om replays te blokkeren.
Verder verkennen
Klaar om uw systemen aan te sluiten?
Maak gratis een account aan in 2 minuten — webhook-configuratie is beschikbaar vanaf het Starter-plan (gratis, 5 enveloppen/maand).