Webhook-Ereignis-Referenz

Detaillierte Payload-Schemas und Beispiel-JSON für jeden ZenFlip-Webhook-Ereignistyp, einschließlich Anfrage-Header und eines Node.js-Signaturverifizierungsbeispiels.

On this page

Webhook-Ereignis-Referenz

Dieses Dokument enthält das vollständige Payload-Schema und Beispiel-JSON für jeden von ZenFlip unterstützten Webhook-Ereignistyp. Alle Payloads folgen derselben übergeordneten Struktur.

Gemeinsame Hüllstruktur

Jeder Webhook-Payload teilt diese Struktur:

`json { "id": "wh_del_{uuid}", "event": "{event_type}", "createdAt": "ISO 8601 timestamp", "organizationId": "{org_uuid}", "data": { } } `

Anfrage-Header

Jede Webhook-HTTP-POST-Anfrage enthält diese Header:

Header

Beispielwert

Content-Type

application/json

User-Agent

ZenFlip-Webhooks/1.0

X-ZenFlip-Event

publication.created

X-ZenFlip-Delivery-Id

wh_del_550e8400-e29b-41d4-a716-446655440000

X-ZenFlip-Signature

a3f2b8c91d... (HMAC-SHA256 Hex-Digest)


publication.created

Wird ausgelöst, wenn eine neue Publikation in der Organisation erstellt wird.

Payload-Schema

Feld

Typ

Beschreibung

data.publicationId

string

UUID der neuen Publikation

data.title

string

Publikationstitel

data.slug

string

URL-freundlicher Slug

data.status

string

Initialstatus (immer draft)

data.createdBy

string

UUID des Benutzers, der sie erstellt hat

Beispiel-Payload

`json { "id": "wh_del_a1b2c3d4-0001-0001-0001-000000000001", "event": "publication.created", "createdAt": "2026-02-20T12:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "slug": "q1-sales-report", "status": "draft", "createdBy": "550e8400-e29b-41d4-a716-446655440000" } } `


publication.converted

Wird ausgelöst, wenn ein PDF-Konvertierungsauftrag abgeschlossen wird, unabhängig davon, ob erfolgreich oder mit einem Fehler.

Payload-Schema

Feld

Typ

Beschreibung

data.publicationId

string

UUID der Publikation

data.title

string

Publikationstitel

data.versionId

string

UUID der konvertierten Version

data.versionNumber

integer

Fortlaufende Versionsnummer

data.status

string

Konvertierungsergebnis: ready oder failed

data.pageCount

integer

Anzahl der generierten Seiten (0 bei Fehler)

data.conversionError

string

Fehlermeldung (null bei Erfolg)

data.convertedAt

string

ISO 8601-Zeitstempel des Konvertierungsabschlusses

Beispiel-Payload (Erfolg)

`json { "id": "wh_del_a1b2c3d4-0002-0002-0002-000000000002", "event": "publication.converted", "createdAt": "2026-02-20T14:05:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "versionId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "versionNumber": 1, "status": "ready", "pageCount": 24, "conversionError": null, "convertedAt": "2026-02-20T14:05:00.000Z" } } `

Beispiel-Payload (Fehler)

`json { "id": "wh_del_a1b2c3d4-0002-0002-0002-000000000003", "event": "publication.converted", "createdAt": "2026-02-20T14:06:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "versionId": "d4e5f6a7-b8c9-0123-def0-234567890123", "versionNumber": 2, "status": "failed", "pageCount": 0, "conversionError": "PDF file is password-protected and cannot be processed", "convertedAt": "2026-02-20T14:06:00.000Z" } } `


publication.deleted

Wird ausgelöst, wenn eine Publikation dauerhaft gelöscht wird.

Payload-Schema

Feld

Typ

Beschreibung

data.publicationId

string

UUID der gelöschten Publikation

data.title

string

Titel zum Zeitpunkt der Löschung

data.slug

string

Slug zum Zeitpunkt der Löschung

data.deletedBy

string

UUID des Benutzers, der gelöscht hat

Beispiel-Payload

`json { "id": "wh_del_a1b2c3d4-0003-0003-0003-000000000004", "event": "publication.deleted", "createdAt": "2026-02-20T16:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "slug": "q1-sales-report", "deletedBy": "550e8400-e29b-41d4-a716-446655440000" } } `


lead.captured

Wird ausgelöst, wenn ein Betrachter ein Lead-Capture-Formular in einer Publikation einreicht.

Payload-Schema

Feld

Typ

Beschreibung

data.leadId

string

UUID des erfassten Leads

data.publicationId

string

UUID der Publikation

data.email

string

Eingereichte E-Mail-Adresse

data.name

string

Vollständiger Name (null wenn nicht erfasst)

data.company

string

Firmenname (null wenn nicht erfasst)

data.phone

string

Telefonnummer (null wenn nicht erfasst)

data.customFields

object

Schlüssel-Wert-Paare aus benutzerdefinierten Formularfeldern

data.sourcePage

integer

Seite, auf der das Formular ausgelöst wurde

data.triggerType

string

Auslöser-Methode: page, exit, timer, scroll

data.capturedAt

string

ISO 8601-Zeitstempel der Einreichung

Beispiel-Payload

`json { "id": "wh_del_a1b2c3d4-0004-0004-0004-000000000005", "event": "lead.captured", "createdAt": "2026-02-20T15:30:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "leadId": "d4e5f6a7-b8c9-0123-def0-234567890123", "publicationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "email": "jane@example.com", "name": "Jane Doe", "company": "Acme Inc.", "phone": "+1-555-0100", "customFields": { "jobTitle": "Product Manager", "department": "Marketing" }, "sourcePage": 3, "triggerType": "page", "capturedAt": "2026-02-20T15:30:00.000Z" } } `


team.member_joined

Wird ausgelöst, wenn ein Teammitglied seine Einladung annimmt und der Organisation beitritt.

Payload-Schema

Feld

Typ

Beschreibung

data.userId

string

UUID des neuen Teammitglieds

data.email

string

E-Mail-Adresse des Mitglieds

data.name

string

Anzeigename

data.role

string

Zugewiesene Rolle: admin, editor, viewer

data.invitedBy

string

UUID des Benutzers, der die Einladung gesendet hat

data.joinedAt

string

ISO 8601-Zeitstempel des Beitritts

Beispiel-Payload

`json { "id": "wh_del_a1b2c3d4-0005-0005-0005-000000000006", "event": "team.member_joined", "createdAt": "2026-02-20T17:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "userId": "880e8400-e29b-41d4-a716-446655440003", "email": "neweditor@example.com", "name": "Jordan Lee", "role": "editor", "invitedBy": "550e8400-e29b-41d4-a716-446655440000", "joinedAt": "2026-02-20T17:00:00.000Z" } } `


export.completed

Wird ausgelöst, wenn ein HTML- oder SCORM-Exportauftrag die Verarbeitung abschließt.

Payload-Schema

Feld

Typ

Beschreibung

data.exportJobId

string

UUID des Exportauftrags

data.publicationId

string

UUID der exportierten Publikation

data.format

string

Exportformat: html, scorm_12, scorm_2004

data.status

string

Ergebnis: completed oder failed

data.downloadUrl

string

Vorsignierte URL zum Herunterladen des Exports (null bei Fehler, läuft nach 24 Stunden ab)

data.fileSizeBytes

integer

Größe der Exportdatei in Bytes (0 bei Fehler)

data.error

string

Fehlermeldung (null bei Erfolg)

data.completedAt

string

ISO 8601-Zeitstempel des Abschlusses

Beispiel-Payload

`json { "id": "wh_del_a1b2c3d4-0006-0006-0006-000000000007", "event": "export.completed", "createdAt": "2026-02-20T18:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "exportJobId": "f6a7b8c9-d0e1-2345-f012-456789012345", "publicationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "scorm_12", "status": "completed", "downloadUrl": "https://cdn.zenflip.io/exports/f6a7b8c9...?token=abc123&expires=1740182400", "fileSizeBytes": 15728640, "error": null, "completedAt": "2026-02-20T18:00:00.000Z" } } `


Signaturverifizierung (Vollständiges Beispiel)

Hier ist ein vollständiger Node.js Express-Server, der ZenFlip-Webhooks empfängt und verifiziert:

`javascript const express = require("express"); const crypto = require("crypto");

const app = express(); const WEBHOOK_SECRET = process.env.ZENFLIP_WEBHOOK_SECRET;

// WICHTIG: express.raw() verwenden, um den Roh-Body für die Signaturverifizierung zu erhalten app.post( "/webhooks/zenflip", express.raw({ type: "application/json" }), (req, res) => { const signature = req.headers["x-zenflip-signature"]; const eventType = req.headers["x-zenflip-event"]; const deliveryId = req.headers["x-zenflip-delivery-id"];

// Schritt 1: Signatur verifizieren const expected = crypto .createHmac("sha256", WEBHOOK_SECRET) .update(req.body, "utf8") .digest("hex");

const isValid = crypto.timingSafeEqual( Buffer.from(signature, "hex"), Buffer.from(expected, "hex") );

if (!isValid) { console.error("Webhook signature verification failed:", deliveryId); return res.status(401).json({ error: "Invalid signature" }); }

// Schritt 2: Payload parsen const event = JSON.parse(req.body.toString());

// Schritt 3: Sofort bestätigen res.status(200).json({ received: true });

// Schritt 4: Ereignis asynchron verarbeiten handleEvent(event).catch((err) => { console.error("Error processing webhook:", err); }); } );

async function handleEvent(event) { switch (event.event) { case "publication.created": console.log("New publication:", event.data.title); break;

case "publication.converted": if (event.data.status === "ready") { console.log( "Conversion complete:", event.data.title, "-", event.data.pageCount, "pages" ); } else { console.error("Conversion failed:", event.data.conversionError); } break;

case "lead.captured": console.log("New lead:", event.data.email, "from page", event.data.sourcePage); // An CRM weiterleiten, Benachrichtigung senden, usw. break;

case "team.member_joined": console.log("New team member:", event.data.name, "as", event.data.role); break;

case "export.completed": if (event.data.status === "completed") { console.log("Export ready:", event.data.downloadUrl); } break;

default: console.log("Unhandled event type:", event.event); } }

app.listen(3000, () => { console.log("Webhook server listening on port 3000"); }); `

Best Practices

  1. Signaturen immer verifizieren. Vertrauen Sie niemals einem Webhook-Payload, ohne den X-ZenFlip-Signature-Header zu validieren.

  2. Schnell antworten. Geben Sie sofort einen 200-Status zurück und verarbeiten Sie das Ereignis asynchron. Webhook-Zustellungen brechen nach 10 Sekunden ab.

  3. Nach Zustellungs-ID deduplizieren. Speichern Sie verarbeitete id-Werte und überspringen Sie Duplikate. Wiederholungsversuche können dazu führen, dass dasselbe Ereignis mehr als einmal zugestellt wird.

  4. HTTPS-Endpunkte verwenden. ZenFlip sendet Webhooks nur an URLs mit HTTPS.

  5. Fehler elegant behandeln. Wenn ein nachgelagerter Dienst nicht verfügbar ist, stellen Sie das Ereignis für spätere Verarbeitung in eine Warteschlange, anstatt einen Fehlerstatus zurückzugeben.

Next →
Webhooks-Übersicht