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.
| Peristiwa | Pemberontakan |
|---|---|
envelope.created | Sebuah sampul dibuat (dengan UI, API atau template) berguna untuk menyinkronkan catatan sisi CRM sejak awal. |
envelope.sent | Peti ini dikirim ke penandatangan (email pertama yang dikirim). |
envelope.completed | Semua penandatangan telah menandatangani. eIDAS PDF yang disegel dan audit trail tersedia melalui `proof_pdf_url` di payload. |
envelope.declined | Seorang penandatangan menolak amplop. Alasan penolakan (jika diberikan oleh penandatangan) ada di `data.decline_reason`. |
envelope.voided | Lembar itu dibatalkan oleh penerbit sebelum tanda tangan lengkap. |
envelope.expired | Tanggal kadaluarsa amplop telah berlalu tanpa tanda tangan lengkap. Daftar penandatangan yang hilang ada di `data.missing_signers`. |
recipient.signed | Satu penandatangan telah menandatangani (tapi tidak semua). |
recipient.viewed | Seorang 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.
| Percobaan | Waktu yang dihabiskan | Waktu yang berlalu |
|---|---|---|
| #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 |
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).