Preskoči na glavnu sadržaj
Certyneo
Dokumentiranje za razvoj

Webhooks primati događaje potpisivanja u stvarnom vremenu

Postavite HTTPS URL na svojoj Certyneo kontrolnoj ploči i dobijate POST potpisani HMAC-SHA256 čim se događaj dogodi na vašim omotnicama: potpis, odbijanje, istek. 8 podržanih događaja, eksponencijalni ponovljeni pokušaj na 6 pokušaja, kriptografska provjera u 8 linija koda.

< 5s

U slučaju da se ne provede isporuka, isporuka se može provesti u skladu s člankom 6. stavkom 1.

5x

U slučaju neuspjeha pokušaji (do ~ 9h kasnije)

HMAC-SHA256

Algorithm za potpisivanje svakog zahtjeva

Katalog događaja

Sljedećih 8 događaja pokriva cijeli životni ciklus Certyneo omotnice.

DogađajIzbijanje
envelope.createdNapravljena je omotnica (po UI-u, API-ju ili predlošku) korisna za sinhronizaciju CRM-a od početka.
envelope.sentKupus je poslat potpisnicima (prva e-pošta poslata).
envelope.completedSvi potpisnici su potpisali. eIDAS pečatirani PDF i trail audit su dostupni preko `proof_pdf_url` u korisnom teretu.
envelope.declinedRazlog za odbijanje (ako ga je podnio potpisnik) nalazi se u `data.decline_reason`.
envelope.voidedKoverta je otkazana od strane izdavatelja prije potpisanog potpisa.
envelope.expiredProšao je rok isteka trajanja koverte bez potpuna potpisa.
recipient.signedKorisno za slijedeće radne procese: pokrenuti prelazak na sljedećeg potpisnika.
recipient.viewedPotpisnik je otvorio link za potpisivanje bez potpisivanja, što je korisno za ciljane trgovinske relapsove.

Format korisnog tereta

Svi događaji dijele isti JSON top-level šema: `event`, `envelope_id`, `occurred_at`, `data`. Sadržaj `data` varira od događaja do događaja (pozivi dokumentirani po događaju u 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"
  }
}

HMAC potpis izračunava se na bruto tijelo kao što je poslato ne mijenjate prespacije, JSON re-parsing često mijenja redoslijed ključeva i prekida provjeru.

Provjerite HMAC potpis

Svaki zahtjev potpisuje se Vašim webhook tajnom (vidljiv samo jednom prilikom kreiranja u kontrolnoj tabli, a zatim šifriran na miru).<timestamp>,v1=<hex_hmac>`. UZVJESNO provjeravajte potpis prije obrade tereta bez ovog koraka, svatko može izmisliti događaj i nazvati vašu krajnju točku.

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)

Česta greška

Ne koristite `===` ili `==` za uspoređivanje očekivanog potpisa s primljenim. Koristite funkciju timing-safe (`crypto.timingSafeEqual` u Node-u, `hmac.compare_digest` u Pythonu).

Politika ponovnog ispitivanja

Ako vaša krajnja točka odgovori na nešto drugo od HTTP 2xx (timeout, 5xx, connection refused), mi ćemo se javiti po eksponencijalnom back-offu. Ukupno 6 pokušaja u ~9 sati. Nakon 6 pokušaja, događaj će biti označen kao `failed` u vašoj webhook dashboardu i poslat će se upozorenje putem e-pošte.

PokušajOdluka KomisijeProšlo vrijeme
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

Ako želite ručno preneti propali događaj, webhook dashboard nudi Re-deliver gumb na svakoj propali isporuci dostupan 7 dana nakon konačnog neuspjeha.

Testiranje bez slanja prave kuverte

Endpoint `/v1/webhooks/test` prihvaća URL i tip događaja, a POSTe je izmišljeni payload potpisan točno kao 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čina za postupanje

  • Ako je to moguće, provjerite HMAC prije čitavanja tijela.
  • Procesiranje svakog `envelope_id` × `event` samo jednom, pohranjujući ID-ove koji su već viđeni u bazi (prevrtanje može ponovno isporučiti isti događaj).
  • Odgovori na HTTP 200 u 5 sekundi, a zatim obradi asinkronno (kuga).
  • Logiranje bruto tijela + potpuni potpis u debug HMAC provjera često ne uspijeva na BOM-u ili nevidljivom bijelu površinu.
  • Prijavit će se dead-letter na događaje `failed` kako bi se manualno prenosili u slučaju incidenta na vašoj strani.
  • U slučaju da se ne primijenjuje, prijenos se može provesti u skladu s člankom 6. stavkom 2.

Da bi se još više

Spremni ste za spajanje sustava?

Stvorite besplatan račun u 2 minute webhook je dostupan na Starter planu (besplatno, 5 koverti/mesec).