Webhooks vastaanottaa allekirjoitustapahtumat reaaliajassa
Aseta HTTPS-URL Certyneon paneelipaneelistallesi ja saat HMAC-SHA256-kirjatun POST-viestin, kun kirjekuoresi tapahtumat tapahtuvat: allekirjoitus, kieltäytyminen, päättyminen. 8 tapahtumaa tuettu, eksponentiaalinen uudelleenkokeilu kuudessa yrityksessä, 8 virkkeen koodin salauskäsittely.
< 5s
Tapahtuman jälkeinen keskimääräinen toimitusaika
5x
Yritykset epäonnistumisesta (kunnes ~ 9 tuntia on kulunut)
HMAC-SHA256
Jokaisen pyynnön allekirjoitusalgoritmi
Tapahtumakartta
Seuraavat 8 tapahtumaa kattavat Certyneon koko elinkaaren.
| Tapahtuma | Se on aiheutunut |
|---|---|
envelope.created | Kirjaimessa on luotu kuori (UI, API tai malli) hyödyllinen CRM-sivuston synkronoimiseksi heti luodussa. |
envelope.sent | Kirjaimeksianto lähetetään allekirjoittajalle (ensimmäinen lähetetty sähköposti). |
envelope.completed | Kaikki allekirjoittajat ovat allekirjoittaneet. eIDAS-tyypillinen PDF ja auditointitulos ovat saatavilla `proof_pdf_url` osoitteessa. |
envelope.declined | Allekirjoittaja on kieltäytynyt lähettämästä kirjekuorta. |
envelope.voided | Kirjaimeksiantaja peruutti kirjekuoren ennen kuin se oli allekirjoitettu. |
envelope.expired | Kirjeen voimassaolon päättymispäivä on kulunut ilman täydellistä allekirjoitusta. |
recipient.signed | Yksittäinen allekirjoittaja on allekirjoittanut (mutta ei välttämättä kaikki). |
recipient.viewed | Allekirjoittaja avasi allekirjoituslinkin allekirjoittamatta vielä. |
Palveluvaraston muoto
Kaikki tapahtumat jakavat saman JSON-tason kaavan: `event`, `envelope_id`, `occurred_at`, `data`.
{
"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"
}
}HMAC-signaatti lasketaan raaka-ainetta vastaan älä muuta väliä, JSON-re-parsing muuttaa usein avainten järjestystä ja rikkoo tarkistuksen.
Tarkista HMAC-sääntö
Jokainen hakemus allekirjoitetaan webhookin salaisuudella (nähtävissä vain kerran laittamisen yhteydessä ja salaistuna silloin kun se on ladattu).<timestamp>V1 =<hex_hmac>Tarkista aina allekirjoitus ennen kuin käsittelet käyttökustannusta.
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)Yleinen virhe
Älä käytä `===` tai `==` vertaamaan odotettua ja vastaanotettua allekirjoitusta. Käytä aikavarmaa toimintoa (`crypto.timingSafeEqual` Node, `hmac.compare_digest` Python).
Uudelleenkäsittelypolitiikka
Jos päätepisteesi vastaa muuhun kuin HTTP 2xx (tilapäaika, 5xx, yhteys evätty), me äänestämme eksponentiaalisella varmuusvarmuudella. Yhteensä 6 yritystä ~9 tunnissa. Kuuden jälkeen tapahtuma merkitään `viisaksi ` verkkokeskustelupalveluasi ja varoitus sähköpostia lähetetään.
| Yritys | Kumoitu määräaika | Aika on kulunut |
|---|---|---|
| #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 |
Jos haluat välittää epäonnistuneen tapahtuman manuaalisesti, webhookin ohjauspaneelissa on painike Toimitus jokaiselle epäonnistuneelle toimitukselle joka on käytettävissä 7 päivän ajan lopullisen epäonnistumisen jälkeen.
Testaus ilman oikeaa kirjekuorta
Lopputulos `/v1/webhooks/test` hyväksyy URL:n ja tapahtumatyypin ja POSTe:n fiktiivisen palkkion, joka on allekirjoitettu aivan kuin oikea.
# 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 käytäntöä
- Tarkista HMAC-signaatti ennen kehon lukemista käytä aikaa säästävää vertailua.
- Käsitellä jokainen `envelope_id` × `event` vain kerran tallentamalla ID:t, jotka on jo nähty pohjaksi (sammutus voi palauttaa saman tapahtuman).
- Vastaus HTTP 200:hen on oltava enintään 5 sekuntia, ja sitten asynkroninen (kiivu) käsittely.
- HMAC-tarkastus epäonnistuu usein näkymättömän BOM:n tai valkoisen tilan vuoksi.
- Liitä kuusi kuoppaa tapahtumien "epäonnistuneiden" tapahtumiin, jotta ne siirtyvät käsin, jos jokin on tapahtunut.
- Suvaitse 5 minuutin viivästys `t=` ja vastaanottotunnin välillä sen jälkeen, hylkää, jotta toistoja ei tapahdu.
- Enemmänkin
Valmiina yhdistämään järjestelmänne?
Luoda ilmainen tili 2 minuutissa webhook-sovellus on saatavilla Starter-suunnitelmassa (ilmainen, 5 kirjekuorta/kuukausi).