Вебхукови примајте догађаје потписивања у реалном времену
Поставите HTTPS URL у вашу Цертинео контролну таблу и добијте HMAC-SHA256 потписан ПОСТ чим се догађај деси на вашим пољетима: потпис, одбијање, истечење. 8 подржаних догађаја, експоненцијални ретреј на 6 покушаја, криптографска верификација у 8 линија кода.
< 5s
Просечно време испоруке након догађаја
5x
Покушаји у случају неуспеха (до ~ 9h касније)
HMAC-SHA256
Алгоритам за потписивање сваког захтева
Каталог догађаја
Осам догађаја испод покривају читав животни циклус Цертинео кутије.
| Подзела | Појава |
|---|---|
envelope.created | Обухват се ствара (по УИ, АПИ или шаблону) користан за синхронизацију CRM-овог снимања од самог стварања. |
envelope.sent | Поручена је поља потписаоцима (прва послана е-пошта). |
envelope.completed | Сви потписаници су потписали. eIDAS затворен PDF и аудит траг су доступни преко `proof_pdf_url` у пјлододу. |
envelope.declined | Причина за одбијање (ако је поднео потписац) је у `data.decline_reason`. |
envelope.voided | Порука је поништена од стране издавача пре него што је потписано. |
envelope.expired | Датум истека на пошту је прошао без потпуног потписа. |
recipient.signed | Употребљив за секвенцијалне радне флове: покретање преласка на следећег потписа. |
recipient.viewed | Један од потписалаца је отворио линк за потписивање без још једног потписа, што је корисно за циљане трговинске понове. |
Формат корисне оптерећења
Сви догађаји деле исти JSON шема на врху нивоа: `event`, `envelope_id`, `occurred_at`, `data`. Садржај `data` варира по догађају (поља документована по догађају у 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"
}
}Пајлоад је енкодиран у УТФ-8, без БОМ-а.
Проверите HMAC потпис
Сваки захтев је потписан са вашом вебхукочком тајном (видивим само једном када се креира у контролној табли, а затим шифровано на одмор).<timestamp>,в1=<hex_hmac>УСИКА проверите потпис пре него што обрадите платно оптерећење. Без ове кораке, било ко може да измисли догађај и позове вашу крајњу тачку.
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)Честог грешка
Не користите `===` или `==` за поређење очекиваног и прихваћеног потписа. Користите функцију "тименг-сеф" (`крипто.тименг-сефЕквал` у Node-у, `hmac.compare_digest` у Python-у). Иначе, временски разлика у поређењу између два потписа постепено открива тајну пацијенту нападачу (тименг атак).
Политике ретрија
Ако ваша крајња тачка одговори на нешто друго него HTTP 2xx (тимеаут, 5xx, connection refused), ми се померимо по експоненцијалном бакофу. Укупно 6 покушаја за ~9 сати. Након 6 покушаја, догађај се означује као `фајлиран` у вашој вебхук табли и шаље се упозорење путем е-поште.
| Покушај | Скупни рок | Прошло време |
|---|---|---|
| #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 |
Ако желите да ручно пренесете неуспешну испоруку, вебхук-наводњач нуди дугме Re-deliver на свако неуспело испоруку доступно 7 дана након коначног неуспеха.
Пробајте без слања стварне пощенке
Идеално за валидацију вашег хендлера у континуираној интеграцији или током локалног развоја (са ngrok или еквивалентним).
# 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 пракса које треба поштовати
- Проверите HMAC потпис ПРЕ читања тела користите време-сигурно поређење.
- Обрађивати сваки `envelope_id` × `event` само једном чувајући ИД-ове који су већ виђени у бази (завршавања могу поново испоручити исти догађај).
- Одговори на HTTP 200 у 5 секунди, а затим обрадуј асинхронно (кука).
- Логирање брута тела + потпуни дебаг потпис HMAC проверка често не успева на BOM или невидљивом белом простору.
- Покључите Dead-letter на `failed` догађаје да бисте ручно ретранслирали у случају инцидента на страни ваше службе.
- Толерисати 5 минута кашњења између `t=` и времена пријем и даље, одбацити да би се блокирали репелије.
Да бисте и даље
Спреман да повежем ваше системе?
Створите бесплатни рачун за 2 минута вебхук је доступан на Стартер плану (безплатно, 5 поља/месец).