Webhooks motta signatur hendelser i sanntid
Konfigurer en HTTPS-URL i Certyneo-dashbordet ditt og motta en HMAC-SHA256-signert POST så snart en hendelse skjer på konvoluttene dine: signatur, avvisning, utløp. 11 hendelser støttet, 5 leveringsforsøk i eksponentiell backoff, kryptografisk verifisering på 8 linjer med kode.
< 5s
Median leveringstid etter hendelsen
5x
Leveringsforsøk totalt, spredt over omtrent 1 t 20 min
HMAC-SHA256
Signaturalgoritme for hver forespørsel
Events katalog
De 12 hendelsene nedenfor dekker hele livssyklusen til en Certyneo-konvolutt. Aktiver de som interesserer deg i Innstillinger → Webhooks, ignorer de andre — abonnementet er granulært per hendelse.
| Begivenhet | Utbrudd |
|---|---|
envelope.created | En konvolutt er opprettet (ved UI, API eller template) nyttig for å synkronisere en CRM-side registrering fra opprettelsen. |
envelope.sent | Konvolutten sendes til underskriverne (første e-post sendt). |
envelope.completed | Alle underskrivere har signert og den forseglede eIDAS PDF-en er lagret. Payloaden inneholder signedDocumentUrl, en forhåndssignert lenke gyldig i 7 dager; ellers, GET /v1/envelopes/id/signed-document, og revisjonsspor via GET /v1/envelopes/id/audit-trail. |
envelope.declined | En underskriver avviste konvolutten. Adressen til avviseren er i `data.declinedBy` og årsaken, hvis den ble oppgitt, i `data.reason`. |
envelope.voided | Konvolutten ble kansellert av utsteder før full signatur. |
envelope.expired | Utløpsdatoen for konvolutten har passert uten fullstendig signatur. Spør GET /v1/envelopes/id for å finne ut hvilke underskrivere som mangler. |
envelope.returned_to_sender | En underskriver sendte konvolutten tilbake til avsenderes for korreksjon, uten å avvise den. Årsaken er i `data.reason` og forfatteren av tilbakesendingen i `data.returnedBy`. |
envelope.resubmitted | Avsenderes korrigerte og sendte deretter en konvolutt som tidligere ble returnert. Markerer gjenopptakelsen av signatursyklusen. |
recipient.signed | En individuell underskriver har signert (men ikke nødvendigvis alle). Nyttig for å spore fremgang og knytte til neste trinn i en sekvensielt arbeidsflyts. Advarsel: den forseglede PDF-en finnes ikke ennå på dette stadiet, selv for den siste underskriveren — en nedlasting som startes her returnerer en HTTP 409. Gå gjennom envelope.completed for dokumentet. |
recipient.viewed | En underskriver åpnet signaturkoblingen uten å signere, noe som er nyttig for målrettede kommersielle oppstart. |
recipient.approved | En godkjenner har validert konvolutten uten å sette en signatur (intern valideringsarbeidsflyt). Adressen er i `data.approvedBy`. |
recipient.bounced | E-postserveren til en mottaker har avslått invitasjonen eller påminnelsen permanent (ikke-eksisterende postkasse, død domene). Mottakeren får statusen BOUNCED og automatiske påminnelser stopper. Adressen finnes i `data.recipientEmail`: korriger den og send omslaget på nytt. |
recipient.signed betyr ikke « dokument tilgjengelig »
recipient.signed sendes for HVER underskriver, når de avslutter signaturen sin — inkludert den siste, før det forseglede PDF-dokumentet blir satt sammen og lagret. Et nedlasting utløst fra denne handleren mottar derfor alltid en HTTP 409 « Signed document not available until the envelope is COMPLETED ». Dette er ikke en feil: det er « ikke helt klart ennå ». Abonner på envelope.completed for å hente dokumentet, og behold recipient.signed for å følge fremgangen (hvem som signerte, og når).
Payloadformat
Alle leveranser deler det samme JSON-skjemaet på toppnivå: `event`, `data` og `timestamp`. Hendelsesnavnet gjentas også i `X-Certyneo-Event`-headeren, som gjør det mulig å rute før vi parser kroppen. Innholdet i `data` varierer avhengig av hendelsen, men forblir alltid et flatt objekt med enkle verdier — aldri en matrise eller nestet objekt. Her er en fullstendig `envelope.completed`-levering.
POST /webhooks/certyneo HTTP/1.1
Host: your.app
Content-Type: application/json
X-Certyneo-Event: envelope.completed
X-Certyneo-Delivery-Id: cm8f2h6a10004qr9k5p2wm3xt
X-Certyneo-Signature: 4f3d1c8b2a9e7f60d5c4b3a2918e7f6d5c4b3a2918e7f6d5c4b3a2918e7f6d5c{
"id": "cm8f2h6a10004qr9k5p2wm3xt",
"event": "envelope.completed",
"data": {
"envelopeId": "cm7x2k9p40001qz8h3f7bn2ld",
"sandbox": false,
"subject": "Contrat de prestation Acme Corp",
"status": "COMPLETED",
"signatureLevel": "ADVANCED",
"aesSignerCount": 2,
"completedAt": "2026-05-27T08:42:13.000Z",
"recipientCount": 2,
"recipients": [
{ "email": "alice@acme.com", "name": "Alice Martin", "role": "SIGNER", "status": "SIGNED" },
{ "email": "bob@acme.com", "name": "Bob Durand", "role": "SIGNER", "status": "SIGNED" }
],
"signedDocumentUrl": "https://storage.certyneo.com/signed/cm7x2k9p4.../contrat.pdf?X-Amz-Expires=604800&X-Amz-Signature=..."
},
"timestamp": "2026-05-27T08:42:13.521Z"
}signedDocumentUrl er en forhåndssignert lenke som er gyldig i 7 dager fra hendelsen: den unngår et andre autentisert anrop for å hente PDF-en. Den er fraværende — og ikke null — hvis forhåndssigneringen mislyktes, eller på en QES-konvolutt som ble fullført uten lagret dokument; gå da gjennom GET /v1/envelopes/id/signed-document, som fortsatt er kilden til sannheten. Vær også oppmerksom på manuell gjenspilling fra dead-letter-køen mer enn 7 dager etter hendelsen: lenken i payloaden er utløpt, API-endepunktet er det ikke.
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 signeres med webhook-hemmeligheten din (vises bare én gang ved opprettelsen av abonnementet). Signaturen overføres i `X-Certyneo-Signature`-headeren: det er HMAC-SHA256 av den rå forespørselskroppen, kodet i heksadesimal, uten prefiks eller tidsstempel. ALLTID verifiser signaturen før du behandler payloaden — uten dette trinnet kan hvem som helst forfalske en hendelse og kalle endepunktet ditt.
Node.js / TypeScript
import crypto from "node:crypto";
export function verifyCertyneoSignature(
rawBody: string,
signatureHeader: string,
secret: string,
): boolean {
// X-Certyneo-Signature = HMAC-SHA256(secret, rawBody), hex-encoded.
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const received = Buffer.from(signatureHeader, "hex");
const computed = Buffer.from(expected, "hex");
// timingSafeEqual throws when the two buffers differ in length —
// a malformed header must return false, not crash the handler.
if (received.length !== computed.length) return false;
// timingSafeEqual to mitigate timing attacks.
return crypto.timingSafeEqual(computed, received);
}Python
import hashlib
import hmac
def verify_certyneo_signature(
raw_body: bytes, signature_header: str, secret: str
) -> bool:
"""Verify a Certyneo webhook signature.
`X-Certyneo-Signature` holds the hex-encoded HMAC-SHA256 of the
raw request body, computed with your webhook secret.
"""
expected = hmac.new(
secret.encode("utf-8"),
raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature_header)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 endepunktet ditt bruker for lang tid på å svare, nekter tilkoblingen eller returnerer en 5xx (eller en 429), gjør vi forsøk på nytt med eksponentiell backoff: 5 forsøk totalt, over ca. 1 t 20 min. Etter det femte forsøket går hendelsen til dead-letter queue — den kan hentes opp og avspilles på nytt fra dashbordet ditt, men blir ikke automatisk forsøkt på nytt. En eksplisitt nekting (401, 403, 404, 410, 422…) blir derimot aldri forsøkt på nytt: svaret ville ikke endres, hendelsen går direkte til dead-letter queue.
| Forsøk | Tidsfrist før prøveprøve | Tid som gått siden hendelsen |
|---|---|---|
| #1 | 0 | 0 |
| #2 | + 1 min | 1 min |
| #3 | + 5 min | 6 min |
| #4 | + 15 min | 21 min |
| #5 | + 1 h | 1 h 21 |
De angitte tidene er minimumsverdier: retry-skanningen styres av en cron, så et forsøk kan derfor starte litt etter den teoretiske tiden. Oppgitte hendelser vises under Webhooks → Feil, med en knapp for manuell avspilling og uten oppbevaringsbegrensning. Et endepunkt som har fem påfølgende permanente feil — eller som returnerer 404 / 410 — blir automatisk deaktivert, og du blir varslet via e-post: reaktiver det når det er rettet, telleren starter fra null (enhver vellykket levering setter det også til null).
Test uten å sende en ekte konvolut
Opprett først et abonnement — svaret inneholder `secret` som trengs for HMAC-verifisering, vises bare én gang. Fra Innstillinger → Webhooks, sender «Test»-knappen deretter en signert POST nøyaktig som en ekte levering og viser deg råsvaret fra serveren din: det er den raskeste måten å validere handleren din på, lokalt via ngrok eller i kontinuerlig integrasjon.
# Create a subscription — "secret" is returned once, store it now.
curl -X POST https://api.certyneo.com/v1/webhooks \
-H "Authorization: Bearer $CERTYNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your.app/webhooks/certyneo",
"events": ["envelope.completed", "recipient.signed"]
}'URL-en må være offentlig tilgjengelig over HTTPS: en privat adresse eller localhost blir avvist av SSRF-beskyttelsen når abonnementet opprettes.
Abonnementene er atskilt etter miljø, som nøklene: en webhook opprettet med en nøkkel sk_test_ mottar bare hendelser fra testkonvolutter, en webhook opprettet med en nøkkel sk_live_ mottar bare hendelser fra ekte konvolutter. En testkonvolutt når derfor aldri produksjons-URL-en din, og hver nyttelast indikerer «sandbox» (true eller false).
6 gode metoder
- Sjekk HMAC-signaturen FØR alle avlesninger av kroppen bruk timing-safe sammenligning.
- Deduplisering på `X-Certyneo-Delivery-Id`-headeren (identisk med `id` i brødteksten) ved å lagre identifikatorer som allerede er sett i databasen — en gjenspilling kan realisere en hendelse som du allerede har behandlet hvis ditt 2xx gikk tapt, med samme identifikator ved hver forsøk.
- Svar HTTP 2xx innen maksimalt 10 sekunder, behandle deretter asynkront (kø). Utover det blir leveringen kuttet og tellesmidt som en feil.
- Logge brutthoden + full signatur i debug HMAC-verifisering mislykkes ofte på en BOM eller usynlig hvitsplass.
- Overvåk siden Webhooks → Feil: du blir varslet via e-post hvis endepunktet ditt er deaktivert, men ikke event for event — hvis en hendelse bruker opp forsøkene sine, må du selv gå og spille den av på nytt.
- Filtrer hendelser ved abonnement heller enn i handleren din, og hold deg under grensen på 5 abonnement per konto.
Du er ikke forpliktet til å hoste et endepunkt for å motta disse hendelsene: koblingen setter opp abonnementet for deg og starter flyten direkte ved en kuvertbegivenhet. Se Certyneo for Power Automate og Microsoft 365.
For å gå videre
Klar til å koble sammen systemene?
Webhooks og REST API er inkludert fra Standard-planen. Opprett kontoen din, generer en `sk_test_`-nøkkel og koble endepunktet ditt i sandkasse før du går til produksjon.