דלג לתוכן ראשי
Certyneo
תיעוד מפתח

לקבל את אירועי החתימה בזמן אמת

קבעו כתובת ה- HTTPS בדאשבורד Certyneo שלכם ותקבלו POST חתום ב- HMAC-SHA256 ברגע שאירוע קורה על המעטפות שלכם: חתימה, דחייה, פסק זמן. 8 אירועים נתמכים, ניסיון חוזר אקספוננציאלי ב-6 ניסיונות, אימות קריפטוגרפי ב-8 שורות קוד.

< 5s

זמן משלוח ממוצע לאחר האירוע

5x

ניסיונות אם נכשלו (עד ~ 9 שעות מאוחר יותר)

HMAC-SHA256

אלגוריתם חתימה של כל בקשה

קטלוג אירועים

8 האירועים הבאים מכסים את כל מחזור החיים של מעטפת Certyneo. הפעל את אלה שאתה מעוניין בהם ב"הגדרות → webhooks", התעלל מהאחרים הרישום הוא גרלנירי לפי אירוע.

אירועהתפרצות
envelope.createdמעטפה נוצרת (באינטרנט אינטרנט, 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`. תוכן ה- `data` משתנה בין אירוע ל אירוע (שדות מתועדים לפי אירוע ברפויקט API). הנה דוגמה של `envelope.completed` מלאה.

{
  "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"
  }
}

החובל משולב ב- UTF-8, ללא BOM. חתימת HMAC מתוחזרת על הגוף הגולמי כפי שנשלח.

בדוק חתימת ה-HMAC.

כל בקשה חתומה עם הסוד שלך (נראה רק פעם אחת בעת יצירת לוח המחוונים, ואז מוצפן בהפסקה). החתימה מועברת בכותרת `Certyneo-Signature` בפורמט `t=<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)

טעות נפוצה

אל תשתמש ב- `===` או ב- `==` כדי להשוות את החתימה הצפויה לבין זו שנלקחה. השתמש בפונקציה של זמן בטוח (`crypto.timingSafeEqual` ב- Node, `hmac.compare_digest` ב- Python). אחרת, הפער בזמן ההשוואה בין שתי חתימות חושף את הסוד לאתגר סבלני (תקיפת זמן).

מדיניות ניסיון מחדש

אם נקודת הסיום שלך מגיבה למשהו אחר מאשר HTTP 2xx (timeout, 5xx, connection refused), אנחנו מצלצלים על פי גיבוי אקפוננציאלי. סך הכל 6 ניסיונות ב ~ 9 שעות. לאחר 6 המקרים האירוע מסומן כ `failed` בדשבורד webhook שלך ושלוח דוא"ל אזהרה.

ניסיוןזמן מצטברזמן עבר
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

אם ברצונך להעביר אישית אירוע נכשל, לוח המחוות של האתר מציע כפתור Re-deliver על כל משלוח נכשל זמין במשך 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` רק פעם אחת, תוך שמירה על ה-IDs שראו בבסיס (ההחזרות יכולות לספק את אותו אירוע שוב).
  • תענה ל-HTTP 200 בתוך 5 שניות לכל היותר, ואז תעבוד באסינכרון (קו). אחרת אתה תהיה בטיימווט ויתחשב כניסיון חוזר.
  • תיעוד גוף גולמי + חתימה מלאה בבעת תיקון בדיקה HMAC לעתים קרובות נכשלה על BOM או חלל לבן בלתי נראה.
  • לחבר דד-לטר שורה על אירועים `failed` כדי להגיש באופן ידני במקרה של אירוע בצד שלך.
  • לסבול 5 דקות של הפער בין `t=` לשעה של קבלת מעבר לכך, לדחות כדי לחסום שידורים חוזרים.

כדי להמשיך הלאה.

מוכנים לחבר את מערכות המערכת שלכם?

ליצור חשבון בחינם ב-2 דקות הפעלה של webhook זמינה בתוכנית ההתחלה (חינם, 5 מעטפות/חודש).