Webhooks prejemate dogodke v realnem času
V svojem nadzornem delu Certyneo nastavite HTTPS URL in prejmete podpisano POST, ki je HMAC-SHA256 takoj, ko se zgodi dogodek na vaših ovojnicah: podpis, zavrnitev, iztek. 8 podprtih dogodkov, eksponentni ponovni poskus na 6 poskusov, kriptografska preverjanje v 8 vrsticah kode.
< 5s
Povprečni čas dostave po dogodku
5x
Poskusi v primeru neuspeha (do ~ 9h kasneje)
HMAC-SHA256
Algoritem za podpis vsakega zahtevka
Katalog dogodkov
8 dogodkov spodaj pokriva celoten življenjski cikel Certyneo ovojnice.
| Dogodek | Izbruh |
|---|---|
envelope.created | Ustvariti je treba povod (z UI, API ali predlogo) uporaben za sinhronizacijo registracije na strani CRM takoj po ustvarjanju. |
envelope.sent | Oznaka za začetek cikla aktivnega podpisa. |
envelope.completed | Vsi podpisniki so podpisali. eIDAS zapečateni PDF in sled avditiranja so na voljo preko `proof_pdf_url` v uporabnem breme. |
envelope.declined | Podpisnik je zavrnil ovojnico. Razlog za zavrnitev (če ga podpiše podpisnik) je v `data.decline_reason`. |
envelope.voided | Pošiljatelj je razveljavil koverto, preden je bila podpisana. |
envelope.expired | Datum izteka roka je potekel brez popolnega podpisa. |
recipient.signed | Uporabno za zaporedne delovne tokove: sprožiti prehod na naslednjega podpisnika. |
recipient.viewed | Podpisnik je odprl podpisno povezavo, a še ni podpisal, kar je koristno za ciljno usmerjene trgovinske pobude. |
Oblika uporabnega bremena
Vsi dogodki imajo enake top-level JSON sheme: `event`, `envelope_id`, `occurred_at`, `data`. Vsebina `data` se razlikuje glede na dogodek (okvirno dokumentirana polja v API referenci).
{
"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"
}
}Podpis HMAC se izračuna na tistem prvotnem tkivu, kot je bil poslan.
Preverite podpis HMAC
Vsak zahtevek je podpisan z vašo webhook skrivnostjo (vidno samo enkrat, ko jo ustvarite v nadzorni plošči, nato šifrirano v mirovanju).<timestamp>,v1=<hex_hmac>Vedno preverite podpis, preden obdelate uporabni bremenik. Brez tega koraka lahko kdorkoli ponovi dogodek in pokliče vašo končno točko.
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)Pogosta napaka
Ne uporabljajte `===` ali `==` za primerjavo pričakovanega in prejetega podpisa. Uporabite funkcijo time-safe (`crypto.timingSafeEqual` v Nodeju, `hmac.compare_digest` v Pythonu).
Politika ponovnega preskusa
Če vaša končna točka odgovori drugače kot HTTP 2xx (timeout, 5xx, connection refused), se odzovejo po eksponentnem back-offu. Šest poskusov v ~ 9 urah. Po šestih se dogodek označi `failed` v vaši webhook deski in pošlje opozorilni e-poštni naslov.
| Poskus | Skupni rok | Čas, ki je minil |
|---|---|---|
| #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 |
Če želite ročno posredovati neuspešni dogodek, ima webhook na napravo gumb Re-deliver na vsaki neuspešni dostavi na voljo 7 dni po dokončnem neuspehu.
Preizkus brez pošiljanja pravega kuverte
Končna točka `/v1/webhooks/test` sprejema URL in tip dogodka ter POŠTE fiktivni naložek, ki je podpisan točno kot pravi.
# 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 načel, ki jih je treba upoštevati
- Preverite podpis HMAC PRED vsakim branjem telesa uporabite časovno varno primerjavo.
- Obdelava vsakega `envelope_id` × `event` enkrat, tako da se shranijo ID, ki so bili že videni v bazi (izbira lahko ponovno prikaže isti dogodek).
- Odgovorite HTTP 200 v največ 5 sekundah, nato pa obdelajte asinkronno (v vrsti).
- Logiranje bruto telesa + polni podpis pri debugiranju preverjanje HMAC pogosto ne uspe na BOM ali nevidnem belem prostoru.
- Vklopite Dead-letter v vrsto dogodkov, ki so se zgodili v primeru nesreče, da se v primeru nesreče v vašem servisu lahko vklopite ročno.
- Dovoljenje za pet minutni odmik med `t=` in sprejemnim časom in pozneje, zavrnitev za blokiranje ponovitev.
Da bi šli dlje
Ste pripravljeni na povezovanje sistemov?
Ustvarite brezplačen račun v 2 minutah nastavitve webhooka so na voljo že na Starter načrtu (besplačno, 5 ovojnic/mesec).