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 σας και θα σταλεί ένα μήνυμα ηλεκτρονικού ταχυδρομείου συναγερμού.
| Προσπάθεια | Συνολική προθεσμία | Χρόνος που πέρασε |
|---|---|---|
| #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 |
Εάν θέλετε να αναμεταδώσετε χειροκίνητα ένα αποτυχημένο συμβάν, το 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 περιτυπάκια/μήνα).