Anar al contingut principal
Certyneo
Document de desenvolupament

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.

EsdevenimentEscollida
envelope.createdEs crea un sobre (per UI, API o template) útil per sincronitzar un registre al costat de CRM des de la creació.
envelope.sentEl sobre s'envia als signants (primer correu electrònic enviat). Marca el començament del cicle de signatura actiu.
envelope.completedTots 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.declinedUn signatari ha rebutjat l'envelope. La raó de rebutjament (si la proporciona el signatari) està en `data.decline_reason`.
envelope.voidedL'envelope ha estat cancel·lat per l'emissor abans de la signatura completa.
envelope.expiredLa 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.signedUn signatari individual ha signat (però no necessàriament tots).
recipient.viewedUn 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.

IntentTemps acumulatTemps transcorregut
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 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).