Μετάβαση στο κύριο περιεχόμενο
Certyneo
Επιστημονική υποστήριξη

Webhooks να λαμβάνετε τα γεγονότα υπογραφής σε πραγματικό χρόνο

Ορίστε ένα HTTPS URL στο Certyneo dashboard σας και θα λάβετε ένα POST υπογεγραμμένο με HMAC-SHA256 μόλις συμβεί κάτι στα φυλλάδια σας: υπογραφή, άρνηση, λήξη. 8 υποστηριζόμενα γεγονότα, εκθετική επαναπροσπάθεια σε 6 προσπάθειες, κρυπτογραφική επαλήθευση σε 8 γραμμές κώδικα.

< 5s

Μέση διάρκεια παράδοσης μετά το συμβάν

5x

Προσπάθειες σε περίπτωση αποτυχίας (μέχρι ~ 9h αργότερα)

HMAC-SHA256

Αλγόριθμος υπογραφής κάθε αίτησης

Καταλόγος γεγονότων

Τα 8 γεγονότα που ακολουθούν καλύπτουν ολόκληρο τον κύκλο ζωής ενός Certyneo περιτύπου.

Ειδικότερα:Επανάληψη
envelope.createdΔημιουργείται ένα φάκελο (μέσω UI, API ή πρότυπου) χρήσιμο για να συγχρονιστεί μια καταγραφή στο CRM από την δημιουργία.
envelope.sentΟ φάκελος αποστέλλεται στους υπογράφοντες (πρώτο email που αποστέλλεται).
envelope.completedΤο σφραγισμένο eIDAS PDF και το audit trail είναι διαθέσιμα μέσω `proof_pdf_url` στο payload.
envelope.declinedΟ λόγος άρνησης (αν παρέχεται από τον υπογράφοντα) βρίσκεται στο `data.decline_reason`.
envelope.voidedΟ φάκελος ακυρώθηκε από τον εκδότη πριν από την πλήρη υπογραφή.
envelope.expiredΗ ημερομηνία λήξης του φακέλου πέρασε χωρίς πλήρη υπογραφή.
recipient.signedΧρήσιμο για τις διαδοχικές ροές εργασίας: να ξεκινάει η μετάβαση στον επόμενο υπογράφοντα.
recipient.viewedΈνας υπογράφων άνοιξε το σύνδεσμο χωρίς να υπογράψει ακόμα, χρήσιμο για στοχευμένες εμπορικές αναζωπυρώσεις.

Φόρμα του φορτίου

Όλα τα γεγονότα μοιράζονται το ίδιο top-level JSON schema: `event`, `envelope_id`, `occurred_at`, `data`. Το περιεχόμενο του `data` ποικίλλει ανάλογα με το γεγονός (πεδία που τεκμηριώνονται ανά γεγονός στην αναφορά 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"
  }
}

Το payload είναι κωδικοποιημένο σε UTF-8, χωρίς BOM. Η υπογραφή HMAC υπολογίζεται στο πρωτότυπο σώμα όπως αποστέλλεται μην αλλοιώνετε τα κενά, η επαναπαραγραφή JSON συχνά αλλάζει την σειρά των κλειδιών και σπάει την επαλήθευση.

Ελέγξτε την υπογραφή HMAC

Κάθε αίτηση υπογράφεται με το webhook μυστικό σας (εμφανίζεται μόνο μία φορά κατά τη δημιουργία στο ταμπλό και μετά κρυπτογραφείται σε ανάπαυση).<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)

Συχνά σφάλματα

Χρησιμοποιήστε μια λειτουργία timing-safe (`crypto.timingSafeEqual` στο Node, `hmac.compare_digest` στο Python). Διαφορετικά, η διαφορά χρόνου σύγκρισης μεταξύ δύο υπογραφών αποκαλύπτει σταδιακά το μυστικό σε έναν ασθενή επιτιθέμενο (timing attack).

Πολιτική επαναπροσδιορισμού

Αν το τερματικό σας σημείο απαντήσει κάτι άλλο εκτός από ένα HTTP 2xx (timeout, 5xx, connection refused), θα αντηχεί με εκθετικό backoff. Συνολικά 6 προσπάθειες σε ~9 ώρες. Μετά από τα 6, το γεγονός σηματοδοτείται ως `failed` στο webhook dashboard σας και θα σταλεί ένα μήνυμα ηλεκτρονικού ταχυδρομείου συναγερμού.

ΠροσπάθειαΣυνολική προθεσμίαΧρόνος που πέρασε
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

Εάν θέλετε να αναμεταδώσετε χειροκίνητα ένα αποτυχημένο συμβάν, το webhook dashboard προσφέρει ένα κουμπί Re-deliver σε κάθε αποτυχημένη παράδοση διαθέσιμο για 7 ημέρες μετά την οριστική αποτυχία.

Δοκιμασία χωρίς να στείλετε πραγματικό φάκελο

Το endpoint `/v1/webhooks/test` δέχεται μια URL και έναν τύπο γεγονότος και POSTe ένα ψεύτικο payload υπογεγραμμένο ακριβώς όπως το πραγματικό.

# 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` γίνεται μόνο μία φορά, αποθηκεύοντας τα ID που έχουν ήδη δει στη βάση (οι αναπαραγωγές μπορούν να επαναφέρουν το ίδιο γεγονός).
  • Αν δεν απαντήσετε σε HTTP 200 μέσα σε 5 δευτερόλεπτα, θα υποβληθείτε σε αναμονή ή θα περάσετε σε χρονική διακοπή και θα θεωρηθείτε ως επαναπροσπάθεια.
  • Εγγραφή του σώματος + πλήρης υπογραφή κατά το debug η επαλήθευση HMAC συχνά αποτυγχάνει σε ένα BOM ή ένα αόρατο λευκό χώρο.
  • Συνδέστε μια ουρά dead-letter σε γεγονότα `failed` για να μεταφέρετε χειροκίνητα σε περίπτωση περιστατικού στην πλευρά της υπηρεσίας σας.
  • Να επιτρέπεται 5λεπτη καθυστέρηση μεταξύ `t=` και της ώρας λήψης πέρα από αυτήν, να απορρίπτεται για να μπλοκάρει τις επαναλήψεις.

Για να πάμε πιο πέρα

Έτοιμοι να συνδέσετε τα συστήματά σας;

Δημιουργήστε έναν δωρεάν λογαριασμό σε 2 λεπτά η εγκατάσταση webhook είναι διαθέσιμη από το σχέδιο Starter (δωρεάν, 5 περιτυπάκια/μήνα).