Gå til hovedinnhold
Certyneo
Utvikler dokumentasjon

Webhooks motta signatur hendelser i sanntid

Sett opp en HTTPS-URL i Certyneo-panelet ditt og få en HMAC-SHA256-signert POST når en hendelse skjer på konvoluttene dine: signatur, avslag, utløp. 8 støttede hendelser, eksponentiell retry på 6 forsøk, krypteringsverifisering på 8 linjer med kode.

< 5s

Median leveringstid etter hendelsen

5x

Forsøk hvis de mislykkes (opp til ~ 9 timer senere)

HMAC-SHA256

Signaturalgoritme for hver forespørsel

Events katalog

De 8 hendelsene nedenfor dekker hele livssyklusen til en Certyneo-kuvert. Aktiver de du er interessert i i Innstillinger → Webhooks, ignorer de andre abonnementet er granulært per hendelse.

BegivenhetUtbrudd
envelope.createdEn konvolutt er opprettet (ved UI, API eller template) nyttig for å synkronisere en CRM-side registrering fra opprettelsen.
envelope.sentKonvolutten sendes til underskriverne (første e-post sendt).
envelope.completedAlle undertegnere har signert. eIDAS-stemplet PDF og audit trail er tilgjengelig via `proof_pdf_url` i payload.
envelope.declinedEn signator har avvist konvolutten. Grunnen til avvisningen (hvis den er oppgitt av signaten) er i `data.decline_reason`.
envelope.voidedKonvolutten ble kansellert av utsteder før full signatur.
envelope.expiredEnkelte av de manglende signaturene er i `data.missing_signers`.
recipient.signedEn enkelt underskriver har signert (men ikke alle).
recipient.viewedEn underskriver åpnet signaturkoblingen uten å signere, noe som er nyttig for målrettede kommersielle oppstart.

Payloadformat

Alle hendelser deler samme topp-nivå JSON-skema: `event`, `envelope_id`, `occurred_at`, `data`. Innholdet i `data` varierer fra hendelse til hendelse (dokumenterte felt per hendelse i API-referansen).

{
  "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"
  }
}

Payloaden er kodet UTF-8, uten BOM. HMAC-signaturen beregnes på råen kroppsform som sendt ikke endre mellomrom, JSON-reparsing endrer ofte nøkkelordningen og bryter verifikasjonen.

Sjekk HMAC-signaturen

Hver forespørsel er signert med webhook-hemmeligheten din (synlig én gang ved opprettelse i dashbordet, deretter kryptert ved hvile).<timestamp>,v1=<hex_hmac>`. Alltid sjekk signaturen før du behandler payload uten dette trinnet kan alle forfalske en hendelse og ringe din endepunkt.

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)

Vanlig feil

Bruk IKKE `===` eller `==` for å sammenligne den ventede signaturen med den mottatte. Bruk en timing-safe funksjon (`crypto.timingSafeEqual` i Node, `hmac.compare_digest` i Python). Ellers avslører tidsforskjellen mellom to signaturer gradvis hemmeligheten til en pasient angriper (timing attack).

Retry-policy

Hvis din sluttpunkt svarer noe annet enn en HTTP 2xx (timeout, 5xx, connection refused), ringer vi på basis av eksponentiell backoff. Totalt 6 forsøk på ~9 timer. Etter 6 blir hendelsen merket `failed` i webhook-dashboardet ditt og en varsling e-post sendes.

ForsøkSamlet tidsromTid som er gått
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

Hvis du vil sende en mislyktet hendelse manuelt, tilbyr webhook-dashboarden en knapp Re-deliver på hver mislyktet levering tilgjengelig i 7 dager etter den endelige mislykkes.

Test uten å sende en ekte konvolut

Endpunktet `/v1/webhooks/test` godtar en URL og en hendelsestype, og POST en fiktiv payload signert akkurat som en ekte.

# 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 gode metoder

  • Sjekk HMAC-signaturen FØR alle avlesninger av kroppen bruk timing-safe sammenligning.
  • Behandle hver `envelope_id` × `event` bare en gang ved å lagre ID-ene som allerede er sett i basen (gjenopprettinger kan levere samme hendelse igjen).
  • Svar HTTP 200 innen 5 sekunder, og deretter behandle asynkront (stiv). Ellers vil du bli timeout og regnes som et nytt forsøk.
  • Logge brutthoden + full signatur i debug HMAC-verifisering mislykkes ofte på en BOM eller usynlig hvitsplass.
  • Koble til en dead-letter-sverg på hendelser `failed` for å manuelt videreføre i tilfelle av en hendelse ved siden av tjenesten din.
  • Tolererer en 5 minutters forsinkelse mellom `t=` og mottaketiden over, avviser for å blokkere replay.

For å gå videre

Klar til å koble sammen systemene?

Opprett en gratis konto på 2 minutter webhook-konfigurasjonen er tilgjengelig allerede på Starter-planen (gratis, 5 konvolutter/måned).