Integrationen
Webhooks an LeadJourney senden
Die vollständige Anleitung, um Events per Webhook an LeadJourney zu senden – der Endpoint, beide Wege der Authentifizierung, alle Felder, die du mitsenden kannst (Identifier, Umsatz, Namen, Custom Fields), wie wiederholte Aufrufe behandelt werden und wie du einen Webhook testest und debuggst, der nicht ankommt.
Ein Webhook ist der Weg, auf dem dein eigenes System LeadJourney mitteilt, dass etwas passiert
ist: Ein Lead ist eingegangen, ein Termin wurde gebucht, ein Deal ist gewonnen. Du sendest einen
POST-Request mit einem kleinen JSON-Body, und LeadJourney ordnet dieses Event dem Ad-Klick zu,
aus dem der Lead entstanden ist.
Das ist der richtige Weg, wenn der Meilenstein in deiner eigenen Software liegt – einem eigenen CRM, deinem eigenen Buchungsformular, einem selbst gebauten Backend – und es keinen nativen Connector gibt, der ihn für dich beobachtet.
Kurz gesagt
- Kopiere Webhook-URL und Postback-Secret aus Settings → Events → Conversion API.
- Sende einen
POSTmitContent-Type: application/json, dem HeaderAuthorization: Bearer <dein Secret>und einem Body mit dem Event-Typ und mindestens einem Identifier (click_id,emailoderphone). - Prüfe das Ergebnis im Events Manager und im API-Postbacks-Log.
Wann du einen Webhook brauchst
- Es gibt einen nativen Connector für das Tool (HubSpot, Pipedrive, Stripe, Calendly und weitere). Dann brauchst du nichts davon – verbinden und Events mappen genügt. Siehe Apps.
- Der Meilenstein liegt in deinem eigenen System. Dein CRM, dein Buchungssystem, dein Backend: Dann sendest du das Event selbst per Webhook. Darum geht es hier.
Bevor du startest
Drei Dinge müssen stehen:
- Das Tracking läuft. Events werden dem Besucher zugeordnet, der auf deine Anzeige geklickt hat – die Klicks müssen also zuerst erfasst werden. Siehe Tracking-Script installieren.
- Das Event existiert. Der
type, den du sendest, muss zu einem Event in deinem Events Manager passen. Jeder Workspace startet mitLeadundWonClient. - Du hast deine Zugangsdaten. Öffne Settings → Events → Conversion API:
| Zugangsdatum | Was es ist |
|---|---|
| Webhook-URL | Der Endpoint, an den du sendest. Sie sieht aus wie https://api.leadjourney.io/api/v1/postback/DEINE-WORKSPACE-ID und enthält deine Workspace-ID bereits. |
| Postback-Secret | Dein Authentifizierungs-Secret, beginnt mit pb_…. Behandle es wie ein Passwort; du kannst es hier neu erzeugen, falls es je nach außen gelangt. |
Das Secret gehört auf deinen Server
Wer das Postback-Secret hat, kann Conversions in deinen Workspace schreiben. Halte es in einer serverseitigen Umgebungsvariable – niemals in Browser-Code, einer Mobile-App oder einem öffentlichen Repository. Soll eine Seite im Browser ein Event auslösen, ruft sie deine eigene Backend-Route auf, und dein Backend setzt den Header und leitet weiter.
Aufbau eines Requests
Jedes Event ist ein POST mit JSON-Body:
curl -X POST "https://api.leadjourney.io/api/v1/postback/DEINE-WORKSPACE-ID" \
-H "Authorization: Bearer DEIN_POSTBACK_SECRET" \
-H "Content-Type: application/json" \
-d '{
"type": "Lead",
"click_id": "8f3c2a1e-4b7d-4c1a-9e2f-6d5b3a7c9e10",
"email": "[email protected]"
}'Das ist der ganze Vertrag: URL + Authorization-Header + JSON-Body. Alles Weitere unten ist
Detail rund um diese drei Teile.
Keine eigenen Header möglich? Token in der URL
Manche Tools können keine eigenen Header senden. Dann hängst du das Secret stattdessen als
token-Query-Parameter an – …/postback/DEINE-WORKSPACE-ID?token=DEIN_POSTBACK_SECRET – und
lässt den Body unverändert. Nutze das nur, wenn der Header wirklich nicht geht: Tokens in URLs
landen leichter in Proxy- und Server-Logs.
Was du mitsenden kannst
| Feld | Typ | Pflicht | Was es bewirkt |
|---|---|---|---|
type | String | Pflicht | Der Event-Name, exakt wie im Events Manager (z. B. Lead, WonClient). |
click_id | String (UUID) | Bedingt | Die Click-ID des Leads – der stärkste Match. Pflicht, wenn email und phone fehlen. |
email | String | Bedingt | Die E-Mail-Adresse des Leads. Pflicht, wenn click_id und phone fehlen. |
phone | String | Bedingt | Die Telefonnummer des Leads. Pflicht, wenn click_id und email fehlen. |
revenue | Zahl | Optional | Der Wert der Conversion. Pflicht bei WonClient. |
cash_collected | Zahl | Optional | Was tatsächlich eingegangen ist, falls du das getrennt vom gebuchten Umsatz führst. |
first_name | String | Optional | Der Vorname des Leads. |
last_name | String | Optional | Der Nachname des Leads. |
custom_fields | Objekt | Optional | Deine eigenen Felder als Key-Value-Paare. Müssen vorher angelegt sein. |
Bleib bei diesen Feldern – sie sind das, was die Conversion API definiert.
Der Event-Typ
type muss zeichengenau zum Event-Namen im Events
Manager passen, Groß- und Kleinschreibung eingeschlossen. Ein
Tippfehler legt kein neues Event an – der Aufruf landet einfach nicht dort, wo du ihn erwartest.
Mindestens ein Identifier
Ein Event ohne Identifier kann nicht zugeordnet werden. Sende, was du hast – am besten alle drei:
click_id– der stärkste Match, weil sie auf genau einen Ad-Klick zeigt. Dein System hat sie nur, wenn du sie beim Anlegen des Leads erfasst hast. Siehe Click-ID auslesen & speichern.email– das Sicherheitsnetz. Sie matcht auch Leads, deren Click-ID verloren ging, etwa weil die Person das Gerät gewechselt hat.phone– wertvoll bei telefonlastigen Funnels. Sende sie im internationalen Format (+49170…), damit dieselbe Person systemübergreifend erkannt wird.
Halte nie ein Event zurück, nur weil die Click-ID fehlt
Ein Lead, der organisch, telefonisch oder auf einem anderen Gerät kam, hat womöglich keine
Click-ID. Sende das Event trotzdem mit email und phone – es wird erfasst und kann weiterhin
zugeordnet werden. Siehe Wie Events einem Lead zugeordnet
werden.
Umsatz
Sende revenue als Zahl, nicht als String, mit Punkt als Dezimaltrennzeichen: 1499.00,
nicht "1.499,00 €". Deine Workspace-Währung
bestimmt, wie diese Zahl in den Reports dargestellt wird.
revenueist Pflicht beiWonClient– ohne den Wert zählt der Deal zwar, trägt aber keinen Umsatz zu deinem ROAS bei.cash_collectedist für Geschäftsmodelle, die einen Vertragswert vorab buchen, aber in Raten einnehmen. Sind beide gleich, kannst du es weglassen.- Bei einer Rückerstattung sendest du ein negatives
revenue, um die Summe zu korrigieren.
Namen
first_name und last_name beeinflussen das Matching nicht – sie reichern den Lead-Datensatz an,
damit du in den Reports erkennst, wer hinter einer Conversion steckt.
Custom Fields
Alles, was für dein Geschäft spezifisch ist – Branche, Tarif, Deal-Größe, Lead-Score – kommt in
custom_fields. Lege jedes Feld einmal unter Settings → Events → Custom Fields an und sende
dann seinen exakten Webhook-Key:
{
"type": "WonClient",
"email": "[email protected]",
"revenue": 1499.00,
"custom_fields": {
"industry": "Software",
"company_size": 100,
"deal_size": 1499.00
}
}Das Feld muss vor dem ersten Senden existieren, und sein Typ muss zum Wert passen – ein String für Textfelder, eine Zahl für Zahlen- und Währungsfelder. Custom Fields werden danach zu Group By- und Filter-Dimensionen in deinen Reports. Siehe Custom Fields.
Beispiel-Payloads
Derselbe Endpoint nimmt jedes Event entgegen – nur der Body ändert sich.
{
"type": "Lead",
"click_id": "8f3c2a1e-4b7d-4c1a-9e2f-6d5b3a7c9e10",
"email": "[email protected]",
"phone": "+491701234567",
"first_name": "Anna",
"last_name": "Schmidt"
}{
"type": "BookedCall",
"click_id": "8f3c2a1e-4b7d-4c1a-9e2f-6d5b3a7c9e10",
"email": "[email protected]"
}Die Click-ID ist die, die du beim Anlegen des Leads am Kontakt gespeichert hast – genau das hält jeden späteren Meilenstein am selben Klick.
{
"type": "WonClient",
"click_id": "8f3c2a1e-4b7d-4c1a-9e2f-6d5b3a7c9e10",
"email": "[email protected]",
"revenue": 1499.00,
"cash_collected": 499.00,
"custom_fields": {
"plan": "Pro"
}
}{
"type": "Refund",
"email": "[email protected]",
"revenue": -1499.00
}Ein negatives revenue korrigiert die Summe. Lege das Event Refund vorher im Events Manager an.
Aus deinem Code senden
Häng dich dort ein, wo der Zustand tatsächlich wechselt – im Formular-Handler, in der Methode, die
einen Deal abschließt, im Webhook-Handler deines Zahlungsanbieters – und sende einen POST pro
Meilenstein:
await fetch(`https://api.leadjourney.io/api/v1/postback/${process.env.LJ_WORKSPACE_ID}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LJ_POSTBACK_SECRET}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "WonClient",
click_id: deal.contact.ljClickId, // die am Kontakt gespeicherte Click-ID
email: deal.contact.email,
revenue: deal.amount,
}),
signal: AbortSignal.timeout(10_000),
});$ch = curl_init("https://api.leadjourney.io/api/v1/postback/" . getenv("LJ_WORKSPACE_ID"));
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("LJ_POSTBACK_SECRET"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"type" => "WonClient",
"click_id" => $deal->contact->lj_click_id,
"email" => $deal->contact->email,
"revenue" => $deal->amount,
]),
]);
curl_exec($ch);
curl_close($ch);import os, requests
requests.post(
f"https://api.leadjourney.io/api/v1/postback/{os.environ['LJ_WORKSPACE_ID']}",
headers={
"Authorization": f"Bearer {os.environ['LJ_POSTBACK_SECRET']}",
"Content-Type": "application/json",
},
json={
"type": "WonClient",
"click_id": deal.contact.lj_click_id,
"email": deal.contact.email,
"revenue": deal.amount,
},
timeout=10,
)curl -X POST "https://api.leadjourney.io/api/v1/postback/$LJ_WORKSPACE_ID" \
-H "Authorization: Bearer $LJ_POSTBACK_SECRET" \
-H "Content-Type: application/json" \
-d '{
"type": "WonClient",
"click_id": "'"$CLICK_ID"'",
"email": "[email protected]",
"revenue": 1499.00
}'Wo du den Aufruf einhängst
| Meilenstein | Wo du dich einhängst |
|---|---|
| Lead entsteht | Im Handler deines Formulars oder deiner Buchungsroute, direkt nachdem der Datensatz gespeichert ist |
| Stufe wechselt (qualifiziert, gebucht, erschienen) | In der Methode oder dem Datenbank-Trigger, der die Stufe ändert |
| Deal gewonnen, Zahlung eingegangen | Im Webhook-Handler deines Zahlungsanbieters oder in der Methode, die den Deal auf gewonnen setzt |
| Rückerstattung | Dort, wo die Erstattung gebucht wird |
Speichere die Click-ID am Kontakt, sobald der Lead entsteht. Jedes spätere Event liest sie dann aus deiner eigenen Datenbank zurück – das hält den kompletten Funnel an einem Klick, auch Wochen später.
Zuverlässigkeit
- Blockiere den Nutzer nicht. Sende den Webhook aus einem Background-Job oder einer Queue, mit einem Timeout von wenigen Sekunden. Ein langsamer Aufruf darf nie eine Registrierung oder einen Checkout verzögern.
- Wiederhole bei Fehlern. Behandle jede Antwort außerhalb von 2xx als Fehlschlag und versuche es mit kurzem Backoff erneut.
- Baue keine eigene Dedup-Logik. Nutze die Event-Modi unten – dann bläht ein wiederholter Aufruf deine Zahlen nicht auf.
Wiederholte Aufrufe: Duplikate und Updates
Wie ein zweiter Webhook für denselben Lead behandelt wird, entscheidest du pro Event im Events Manager, nicht in deinem Code:
| Modus | Was passiert |
|---|---|
| Create new conversion | Jeder Webhook wird als neues Event erfasst – etwa für Wiederkäufe. |
| Ignore duplicate postbacks | Nur das erste Event pro Lead wird behalten. Ideal für Lead, damit ein doppelter Submit nicht doppelt zählt. |
| Edit conversion with new data | Das bestehende Event wird aktualisiert – für einen Umsatzwert, der später korrigiert wird. |
| Keep original data, increment payout | Das ursprüngliche Event bleibt, der neue Umsatz kommt obendrauf (nur WonClient). |
Den Modus legst du beim Anlegen des Events fest – siehe Events & Events Manager.
Testen und debuggen
Einen Klick zum Matchen erzeugen
Öffne deine Seite über einen getrackten Link – eine Test-Anzeigen-URL oder einen Link aus dem UTM Builder –, damit es einen Klick gibt, an den sich das Event hängen kann.
Ein Test-Event senden
Sende einen Webhook (das cURL-Beispiel oben genügt). Unter Settings → Events steigt der Total-Zähler des Events, Last Received aktualisiert sich, und der Status wechselt von Created auf Active.
Den rohen Aufruf ansehen
Öffne das API-Postbacks-Log. Es zeigt Statuscode, den gesendeten Payload und ob der Aufruf über Klick, E-Mail oder Telefon gematcht hat. Hier debuggst du einen Webhook, der nicht ankommt.
Wenn etwas nicht ankommt
| Symptom | Übliche Ursache |
|---|---|
| Event bleibt auf Created / Never received | Es erreicht LeadJourney gar nichts: falsche URL, fehlende Workspace-ID, oder der Aufruf feuert nie |
| Antwort meldet fehlende Berechtigung | Falsches oder neu erzeugtes Postback-Secret, oder der Header fehlt – prüfe Authorization: Bearer pb_… |
| Aufruf kommt an, aber in den Reports taucht nichts auf | Der type passt nicht exakt zum Event-Namen, oder es wurde kein Identifier gesendet |
| Conversion erfasst, aber nicht zugeordnet | Keine click_id, und E-Mail/Telefon passen zu keinem getrackten Besucher – siehe Fehlende Leads |
| Zahlen zu hoch | Der Modus des Events ist Create new conversion, während dein System Retries sendet – wechsle auf Ignore duplicate postbacks |
FAQ
Verwandte Artikel
Events aus einem eigenen CRM senden
Die Entwickler-Strecke: Pipeline-Stufen auf Events mappen und aus dem Backend feuern.
Events & Events Manager
Events anlegen, Modus wählen und sehen, wie sie einem Lead zugeordnet werden.
Click-ID auslesen & speichern
Die Click-ID aus dem Browser holen und an deinen Datensätzen speichern.
Custom Fields
Eigene Daten mit einem Event senden und darüber reporten.
API-Postbacks-Log
Jeden Aufruf ansehen: Payload, Statuscode und ob er gematcht hat.
Apps
Verbinde externe Apps, damit ihre Events als Conversions in LeadJourney fließen, automatisch den Kampagnen zugeordnet, die sie gebracht haben. CRMs wie HubSpot, Salesforce und GoHighLevel, Zahlungen und Shops, Formulare, Buchungstools, Call-Tracking, Webinare und ein BigQuery-Export.
Events aus einem eigenen CRM senden
Eine Entwickler-Referenz für die Conversion API. Sende ein Webhook (Postback) aus deinem eigenen CRM oder Backend, sobald sich eine Pipeline-Stufe ändert – LeadJourney ordnet es dem Ad-Klick zu. Inklusive Endpoint, Authentifizierung, vollständiger Payload-Referenz und kopierfertiger Beispiel-Requests in cURL, Node.js, PHP und Python.