Pereiti prie pagrindinio turinio
Certyneo
Kūrėjo dokumentacija

Webhooks gaukite realiu laiku pasirašymo įvykius

Įrinkite HTTPS URL savo "Certyneo" lentelėje ir gaukite HMAC-SHA256 pasirašytą POST, kai tik įvyks įvykis ant jūsų užrašų: pasirašymas, atsisakymas, galiojimo pabaigos. 8 palaikomi įvykiai, 6 bandymų eksponentiškas bandymas, 8 kodo eilutės kriptografijos patikra.

< 5s

Vidutinis pristatymo laikas po įvykio

5x

Bandymai, jei nepavyksta (iki ~ 9 h vėliau)

HMAC-SHA256

Kiekvienos prašymo parašo algoritmas

Renginių sąrašas

8 toliau išvardyti įvykiai apima visą Certyneo užrašų gyvenimo ciklą. Įjungkite tuos, kurie jus domina, nustatymuose → "Webhooks", ignoruokite kitus prenumeratos yra granuliarūs pagal įvykį.

PasirodymasIšpuolis
envelope.createdĮrašas sukuriamas (UI, API ar šablonu) naudingas sinchronizuoti įrašą CRM pusėje nuo pat kūrimo.
envelope.sentUžrašas išsiunčiamas pasirašiusiems asmenims (pirmas išsiųstas el. paštas).
envelope.completedVisi pasirašę asmenys pasirašė. eIDAS uždaryto PDF ir audito pėdsakoje galima rasti naudotojo naudotojo `proof_pdf_url`.
envelope.declinedParašytojas atsisakė užrašų, o priežastis (jei ją pateikia parašytojas) pateikiama `data.decline_reason`.
envelope.voidedUžrašas buvo atšauktas išdavėjo iki visiško pasirašymo.
envelope.expiredUžrašas baigėsi pasibaigus galiojimo terminui, jei nebuvo pasirašyta visos jo dalys.
recipient.signedVienas pasirašęs (bet nebūtinai visi) naudingas sekmenciniams darbai: perkelti į kitą pasirašėją.
recipient.viewedVienas iš pasirašytojų atvėrė pasirašymo nuorodą, dar nepasirašęs, naudinga tikslinėms prekybos paleidimams.

Naudotojo krovinio formatas

Visi įvykiai turi tą pačią aukščiausio lygio JSON schemą: `event`, `envelope_id`, `occurred_at`, `data`. `data` turinys skiriasi pagal įvykį (API nuorodą pagal įvykį dokumentuoti laukai).

{
  "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 parašas apskaičiuojamas ant žalio kūno, kaip išsiųstas.

HMAC parašo patikra

Kiekvienas užklausas pasirašomas jūsų "webhook" paslaptimi (įrodytu tik kartą sukuriant jį į skydliauką, o paskui užšifruotas po to).<timestamp>,v1=<hex_hmac>Visada patikrinkite parašą prieš apdorojant naudotinį krovinį. Be šio žingsnio kiekvienas gali suklastoti įvykį ir skambinti jūsų galiniam taškai.

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)

Dažnas klaidas

Naudokite funkciją "timing-safe" (`crypto.timingSafeEqual` Node, `hmac.compare_digest` Python), nes jei to nepadarysite, dviejų parašų palyginimo laiko skirtumas palaipsniui atskleis paslaptį paciento puolikui (timing attack).

Perbandymo politika

Jei jūsų pasiekiamasis taškas atsako kitaip nei HTTP 2xx (išleidimas, 5xx, atsisakyta ryšys), mes iškvietame eksponentinį atsarginįjįjįjįjį. Iš viso 6 bandymai ~9 valandos. Po 6 bandymų įvykis yra pažymėtas "fallowed" jūsų interneto ryšio skydelyje ir išsiunčiamas įspėjamasis el. paštu.

BandymasBendras laikotarpisPraėjusį laiką
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

Jei norite rankiniu būdu perduoti pralaimėtą įvykį, "webhook" lentyninėje yra mygtukas " Perduoti" kiekvienam pralaimėjusiam pristatymui, kuris yra prieinamas 7 dienas po galutinio pralaimėjimo.

Bandymas be tikro užrašų siuntimo

Galutinis taškas `/v1/webhooks/test` priima URL ir įvykio tipą, ir POSTe fiktyvų naudotinį krovinį, pasirašytą kaip tikras.

# 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 praktikos, kurias reikia laikytis

  • HMAC parašas tikrinamas prieš skaitant kūną naudoti laikui pasiekiamą palyginimą.
  • Kiekviena `envelope_id` × `event` apdorojama tik vieną kartą, saugant ID, jau matytus bazėje (išgręžimai gali atkurti tą patį įvykį).
  • Atsakykite į HTTP 200 per 5 sekundes ir tada tvarkykite asinkroniškai (užsisakykite uoliai).
  • Nėra jokių įrodymų, kad HMAC patikra yra nesuderinama su BOM arba nematoma baltos erdvės.
  • Įjungti "dead-letter" eilę į įvykius "negerai" ir perkelti rankiniu būdu, jei įvyks incidentas jūsų tarnyboje.
  • Toleruoti 5 minučių atostogą nuo `t=` iki priėmimo laiko, atmetti, kad būtų užblokuoti transliacijos.

Norėdami eiti toliau

Ar pasiruošę prisijungti prie savo sistemų?

Sukurkite nemokamą paskyrą per 2 minutes "webhook" konfigūracija yra prieinama nuo pat "Starter" plano (nemokama, 5 užrašai per mėnesį).