Webhooks получавайте събития за подписване в реално време
Настрои HTTPS URL в таблото за данни Certyneo и получи подписан POST HMAC-SHA256 веднага щом се случи нещо на вашите пликове: подписване, отказ, изтичане на срока. 8 събития, поддържани, експоненциален ретроу на 6 опита, криптографска проверка с 8 реда код.
< 5s
Средна продължителност на доставката след събитието
5x
Опити при неуспех (до ~ 9 часа по-късно)
HMAC-SHA256
Алгоритм за подписване на всяко заявление
Каталог на събитията
8 събития по-долу покриват целия жизнен цикъл на един Certyneo плик.
| Събитие | Избухване |
|---|---|
envelope.created | Създава се плик (по UI, API или шаблон) полезен за синхронизиране на запис на 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"
}
}Пайлоудът е кодиран UTF-8, без BOM. Подписът HMAC се изчислява върху грубото тяло, както е изпратено не променяйте пространствата, JSON ре-парсирането често променя реда на ключовете и нарушава проверката.
Проверка на HMAC подписа
Всеки запитване се подписва с вашия уебхуков секрет (видим само веднъж при създаване в таблото, след което е криптиран в почивка).<timestamp>,v1=<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)Често срещана грешка
Използвайте функция за време-безопасност (`crypto.timingSafeEqual` в Node, `hmac.compare_digest` в Python). В противен случай, разликата в времето на сравнение между двата подписа постепенно разкрива тайната на пациент атакуващ (тиминг атака).
Политика за повторни опити
Ако крайната точка отговори с нещо друго, освен HTTP 2xx (timeout, 5xx, connection refused), ние ще се включим в експоненциален backoff. Общо 6 опити за ~9 часа. След 6 опита събитието се маркира `failed` в таблото на вашия webhook и се изпраща предупредителен имейл.
| Опит | Струва се, че | Изминало време |
|---|---|---|
| #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 дни след окончателния провал.
Изпитване без изпращане на истински плик
Крайната точка `/v1/webhooks/test` приема URL и тип на събитие, а ПОСТИВА фиктивен полезен товар, подписан точно като истински.
# 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` само веднъж, като се съхраняват вече видяните ID в базата (възвръщането може да възстанови същия събитие).
- Отговори на HTTP 200 в рамките на 5 секунди, а след това обработи асинхронно (в опашка).
- Logger raw body + full debug signature HMAC проверката често се проваля при BOM или невидими бели пространства.
- Включете опашка за "неуспешни" събития, за да прехвърляте ръчно в случай на инцидент на вашия отдел.
- Търпи 5 минути откъсване между `t=` и времето на приемане и след това, отхвърляне, за да блокира ретрансляциите.
Да продължим
Готови ли сте да свържете системите си?
Създайте безплатен акаунт за 2 минути настройката на webhook е налична от стартиращия план (безплатно, 5 пликове/месец).