Перейти до основного вмісту
Certyneo
Документація розробника

Веб-буки отримуйте підпис події в реальному часі

Налаштуйте HTTPS URL в панелі даних Certyneo і отримаєте підписану HMAC-SHA256 POST, як тільки відбудеться подія на ваших конвертах: підпис, відмова, термін дії. 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Дата закінчення дії конверту пройшла без повного підписання. Список відсутніх підписантів знаходиться в `data.missing_signers`.
recipient.signedІндивідуальний підписник підписав (але не обов'язково всі).
recipient.viewedПідписант відкрив підписний посилання, не підписуючи його, що дуже корисно для цілеспрямованого відновлення торгівлі.

Формат корисної навантаження

Усі події поділяють одну і ту ж JSON-потужню схему: `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 обчислюється на грубому тілі, як відправлено не змінюйте пробіли, перепараси 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)

Часточутні помилки

Не використовуйте `===` або `==` для порівняння очікуваного та отриманого підписів. Використовуйте функцію timing-safe (`crypto.timingSafeEqual` в Node, `hmac.compare_digest` в Python).

Політика перепробування

Якщо ваша кінцева точка відповідає іншим, ніж HTTP 2xx (завтра, 5xx, відмовлено у зв'язку), ми відгукуємося за експоненційним резервним кодом. Всього 6 спроб за ~9 годин. Після 6 подій позначено `failed` в вашому веб-дослідному панелі і відправляється повідомлення електронною поштою.

СпробаСкладенький термінПройшов час
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

Якщо ви хочете вручну передати невдалий екземпляр, веб-дошка пропонує кнопку Передати на кожну невдалу доставку доступну протягом 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 секунд, а потім обробка асинхронна (в черзі).
  • Зверніть до нього повний документ, який ви маєте.
  • Підключити очередний запис на події `failed`, щоб відправити його вручну у разі інциденту на стороні вашого відділу.
  • Допустіть відставання 5 хвилин між `t=` і часом прийому і далі, відкинути, щоб заблокувати ретрансляції.

Щоб піти далі

Готові підключити свої системи?

Створіть безкоштовну акаунт за 2 хвилини налаштування webhook доступне з Starter плану (безкоштовно, 5 конвертів/місяць).