Lompat ke konten utama
Certyneo
Dokumen Pengembang

Webhooks menerima acara tanda tangan secara real time

Atur URL HTTPS di dasbor Certyneo Anda dan terima POST yang ditandatangani HMAC-SHA256 saat suatu peristiwa terjadi pada sampul surat Anda: tanda tangan, penolakan, kadaluarsa. 8 peristiwa didukung, percobaan ulang eksponensial pada 6 upaya, verifikasi kriptografi dalam 8 baris kode.

< 5s

Waktu pengiriman rata-rata setelah peristiwa

5x

Percobaan gagal (sampai ~ 9 jam kemudian)

HMAC-SHA256

Algoritma penandatanganan setiap permintaan

Catalogue of Events (Katalog Acara)

8 peristiwa di bawah ini mencakup seluruh siklus hidup sebuah Certyneo. Aktifkan yang Anda minati di Setelan → Webhooks, abaikan yang lain langganan bergrellar per acara.

PeristiwaPemberontakan
envelope.createdSebuah sampul dibuat (dengan UI, API atau template) berguna untuk menyinkronkan catatan sisi CRM sejak awal.
envelope.sentPeti ini dikirim ke penandatangan (email pertama yang dikirim).
envelope.completedSemua penandatangan telah menandatangani. eIDAS PDF yang disegel dan audit trail tersedia melalui `proof_pdf_url` di payload.
envelope.declinedSeorang penandatangan menolak amplop. Alasan penolakan (jika diberikan oleh penandatangan) ada di `data.decline_reason`.
envelope.voidedLembar itu dibatalkan oleh penerbit sebelum tanda tangan lengkap.
envelope.expiredTanggal kadaluarsa amplop telah berlalu tanpa tanda tangan lengkap. Daftar penandatangan yang hilang ada di `data.missing_signers`.
recipient.signedSatu penandatangan telah menandatangani (tapi tidak semua).
recipient.viewedSeorang penandatangan membuka tautan tanda tangan tanpa menandatangani, berguna untuk promosi bisnis yang ditargetkan.

Format muatan

Semua event berbagi skema JSON top-level yang sama: `event`, `envelope_id`, `occurred_at`, `data`. Isi dari `data` bervariasi menurut event (bidang yang didokumentasikan per event dalam referensi 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"
  }
}

HMAC ditandatangani pada body raw seperti yang dikirim jangan mengubah spasi, re-parsing JSON sering mengubah urutan kunci dan merusak verifikasi.

Periksa tanda tangan HMAC

Setiap permintaan ditandatangani dengan webhook rahasia Anda (terlihat sekali saat membuat di dasbor, kemudian dienkripsi saat istirahat).<timestamp>,v1=<hex_hmac>`. SEMUA PERTAMA periksa tanda tangan sebelum memproses muatan tanpa langkah ini, siapa pun dapat memalsukan peristiwa dan memanggil titik akhir Anda.

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)

Kesalahan yang Sering Terjadi

Jangan menggunakan `===` atau `==` untuk membandingkan tanda tangan yang diharapkan dengan yang diterima. Gunakan fungsi timing-safe (`crypto.timingSafeEqual` di Node, `hmac.compare_digest` di Python).

Kebijakan pencobaan ulang

Jika endpoint Anda menjawab sesuatu selain HTTP 2xx (timeout, 5xx, connection refused), kami berbunyi dengan back-up eksponensial. Total 6 kali dalam ~9 jam. Setelah 6 kali, acara tersebut ditandai `failed` di webhook dashboard Anda dan email peringatan dikirim.

PercobaanWaktu yang dihabiskanWaktu yang berlalu
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

Jika Anda ingin mengirimkan ulang secara manual, webhook memiliki tombol Re-deliver pada setiap pengiriman yang gagal tersedia selama 7 hari setelah kegagalan akhir.

Uji coba tanpa mengirim amplop asli

Endpoint `/v1/webhooks/test` menerima URL dan tipe acara, dan POSTe payload fiksi yang ditandatangani persis seperti yang sebenarnya.

# 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 Kebijakan yang Harus Dipatuhi

  • Periksa tanda tangan HMAC SEBUJUKAN membaca body gunakan perbandingan yang aman waktu.
  • Mengolah setiap `envelope_id` × `event` hanya sekali dengan menyimpan ID yang sudah dilihat di basis (pengulangan dapat mengembalikan peristiwa yang sama).
  • Tanggapan HTTP 200 dalam waktu 5 detik maksimum, kemudian proses asinkron (antrian). Jika tidak, Anda akan di timeout dan dihitung sebagai mencoba lagi.
  • Log body brut + tanda tangan lengkap dalam debug verifikasi HMAC sering gagal pada BOM atau ruang putih yang tidak terlihat.
  • Tambahkan dead-letter ke acara `failed` untuk diteruskan secara manual jika terjadi insiden di sisi departemen Anda.
  • Tolerasi 5 menit dari waktu `t=` dan waktu penerimaan di luar, menolak untuk memblokir tayangan ulang.

Untuk Lebih Lanjut

Siap untuk menghubungkan sistemmu?

Buat akun gratis dalam waktu 2 menit konfigurasi webhook tersedia di Starter plan (gratis, 5 amplop/bulan).