Webhooks rebre els esdeveniments de signatura en temps real
Configureu una URL HTTPS al vostre tauler Certyneo i rebeu un POST signat HMAC-SHA256 quan es produeix un esdeveniment en els vostres sobre: signatura, rebutjament, expiració. 8 esdeveniments recolzats, reintrip exponencial en 6 intents, verificació criptogràfica en 8 línies de codi.
< 5s
Temps de lliurament mitjà després de l'esdeveniment
5x
Intents en cas de fracàs (fins a ~9h després)
HMAC-SHA256
Algoritme de signatura de cada sol·licitud
Catàleg d'esdeveniments
Els 8 esdeveniments que es mostren a continuació cobreixen el cicle de vida complet d'un sobre Certyneo.
| Esdeveniment | Escollida |
|---|---|
envelope.created | Es crea un sobre (per UI, API o template) útil per sincronitzar un registre al costat de CRM des de la creació. |
envelope.sent | El sobre s'envia als signants (primer correu electrònic enviat). Marca el començament del cicle de signatura actiu. |
envelope.completed | Tots els signants han signat. El PDF segellat eIDAS i la pista d'auditoria estan disponibles a través de `proof_pdf_url` al payload. |
envelope.declined | Un signatari ha rebutjat l'envelope. La raó de rebutjament (si la proporciona el signatari) està en `data.decline_reason`. |
envelope.voided | L'envelope ha estat cancel·lat per l'emissor abans de la signatura completa. |
envelope.expired | La data d'expiració de l'envelope ha passat sense signatura completa. La llista de signadors que es troben desapareguts es troba a `data.missing_signers`. |
recipient.signed | Un signatari individual ha signat (però no necessàriament tots). |
recipient.viewed | Un signatari ha obert el enllaç de signatura sense signar encara, útil per a reactivar el comerç. |
Format de la càrrega útil
Tots els esdeveniments comparteixen el mateix esquema JSON de nivell superior: `event`, `envelope_id`, `occurred_at`, `data`. El contingut de `data` varia segons l'esdeveniment (camps documentats per esdeveniment a la referència 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"
}
}La signatura HMAC es calcula sobre el cos brut tal com s'envia no alterar els espais, el re-parsing JSON sovint canvia l'ordre de les claus i trenca la verificació.
Verifica la signatura HMAC
Cada petició és signada amb el teu secret webhook (visible una sola vegada quan es crea al taulell, i després encriptat a descans).<timestamp>,v1=<hex_hmac>`. Sempre comproveu la signatura abans de processar la càrrega útil sense aquest pas, qualsevol pot forjar un esdeveniment i trucar al vostre punt final.
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)Error freqüent
No utilitzeu `===` o `==` per comparar la signatura esperada amb la rebutja. Utilitzeu una funció de temps segur (`crypto.timingSafeEqual` en Node, `hmac.compare_digest` en Python).
Política de retry
Si el teu punt final respon alguna cosa més que un HTTP 2xx (timeout, 5xx, connexió rebutjada), ens retorna un back-off exponencial. Un total de 6 intents en ~9 hores. Al cap de les 6, l'esdeveniment es marca `failed` al teu webhook i s'envia un correu electrònic d'alerta.
| Intent | Temps acumulat | Temps transcorregut |
|---|---|---|
| #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 |
Si voleu relargar manualment un esdeveniment fallit, el webhook del tauler de control ofereix un botó Re-deliver a cada lliurament fallit disponible durant 7 dies després del falliment definitiu.
Provar sense enviar un sobre real
L'endpoint `/v1/webhooks/test` accepta una URL i un tipus d'esdeveniment, i POSTe una càrrega fictícia signada exactament com una veritable. Ideal per validar el seu manipulador en integració contínua o durant el desenvolupament local (amb ngrok o equivalent).
# 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 pràctiques a seguir
- Verificar la signatura HMAC abans de qualsevol lectura del cos utilitzar una comparació de temps segur.
- Processar cada `envelope_id` × `event` només una vegada, guardant els IDs ja vistos a la base (les retries poden tornar a lliurar el mateix esdeveniment).
- Respon a HTTP 200 en 5 segons màxim, i després processa en asíncrona (cola). Si no, estaràs en temps mort i comptat com a intent de tornada.
- Registre el cos brut + la signatura completa en depuració la verificació HMAC sovint falla en un BOM o un espai blanc invisible.
- Connectar una cua de dead-letter als esdeveniments `failed` per relargar manualment en cas d'incident al costat del seu servei.
- Tolerar un desnivell de 5 minuts entre `t=` i l'hora de recepció més enllà, rebutjar per bloquejar les repeticions.
Per anar més enllà
Estan preparats per connectar els seus sistemes?
Crea un compte gratuït en 2 minuts la configuració de webhook està disponible des del pla Starter (gratuït, 5 sobres/mès).