Deinen kompletten SaaS-Funnel tracken (Sign-up → Trial → Kauf)
Die komplette Anleitung für SaaS-Produkte – ordne jede Stufe deines Lifecycles dem Werbe-Klick, Post oder der E-Mail zu, die ihn gebracht hat. Tracke Demo-Calls und Free-Sign-ups, dann Trial gestartet, Trial beendet und den bezahlten Kauf. Käufe kommen automatisch über Stripe rein; die In-Product-Meilensteine sendest du mit einer winzigen Server-Route. Die Kernidee: die Click-ID einmal beim Sign-up erfassen und bei jedem späteren Event erneut mitschicken.
Bei einem SaaS-Produkt verteilen sich die spannenden Momente über Tage oder Wochen und fast alle passieren in deiner App, nicht auf einer Marketing-Seite: jemand bucht eine Demo, erstellt ein kostenloses Konto, startet einen Trial, der Trial endet und – irgendwann – zahlt er. Diese Anleitung verbindet jede dieser Stufen mit LeadJourney, damit du pro Kampagne, Anzeigengruppe und Anzeige nicht nur siehst, wie viele Sign-ups eine Anzeige gebracht hat, sondern wie viele davon zu Trials und zahlenden Kunden wurden.
Tracking muss zuerst live sein
Jede Stufe wird dem Besucher zugeordnet, der auf deine Anzeige geklickt hat – das Tracking-Script muss also zuerst auf deiner Seite laufen. Falls nicht, beginne mit Tracking-Script einbinden (und den Entwickler-Hinweisen unter Auf einer custom-coded Website einbinden).
Der SaaS-Funnel, gemappt auf LeadJourney
Wähle die Stufen, die es in deinem Produkt gibt, und lass den Rest weg. Jeder Event-Name ist nur der
type, den du sendest – benenne sie um in das, was dein Team ohnehin sagt.
| Funnel-Stufe | Event-type | Wie du es sendest | Umsatz? |
|---|---|---|---|
| Demo-Call gebucht (sales-assisted) | BookedCall | Webhook deines Schedulers (Calendly, …) | — |
| Free-Sign-up / Konto erstellt | FreeSignup | Eine winzige Server-Route (Conversion API) | — |
| Trial gestartet | TrialStarted | Dieselbe Server-Route | — |
| Trial beendet | TrialEnded | Dieselbe Server-Route (aus deiner Billing-Logik / einem Cron) | — |
| Kauf (Trial wird bezahlt) | WonClient | Stripe-Integration – automatisch | ✓ |
Zwei Wege, eine Stufe zu senden – und wann welcher
- Kauf → nutze die Stripe-Integration. Echte Geldereignisse tragen den Umsatz und ordnen sich selbst zu, ohne Code. Das ist der einfache Teil.
- Demo-Call → nutze den Webhook deines Schedulers. Die meisten Buchungstools (Calendly, SavvyCal, …) können bei einer Buchung eine URL anpingen.
- Free-Sign-up, Trial gestartet, Trial beendet → sendest du selbst mit einer kleinen Server-Route. Das sind In-Product-Zustandswechsel, die nur deine App kennt – also definierst du den Trigger und postest das Event an LeadJourneys Conversion API.
Die eine Idee, die SaaS-Tracking funktionieren lässt
Hier ist der Haken, der für SaaS einzigartig ist – und das Wichtigste in dieser Anleitung:
Die Click-ID liegt beim Sign-up im Browser. Aber Trial gestartet, Trial beendet und Kauf passieren später – oft serverseitig, in einem Webhook oder einem nächtlichen Cron, ohne Browser und ohne
localStorage.
Du kannst die Click-ID also nicht erneut auslesen, wenn diese späteren Events feuern. Die Lösung ist einfach:
Die Click-ID einmal erfassen, am User speichern, für immer wiederverwenden
Beim Sign-up liest du localStorage.getItem('clickId') und speicherst sie am User-/
Konto-Datensatz in deiner eigenen Datenbank (eine Spalte wie lj_click_id). Jedes spätere
Lifecycle-Event – Trial gestartet, Trial beendet, sogar ein manuelles Upgrade – liest diesen Wert
zurück und schickt ihn mit. Einmal erfassen, für die ganze Customer Journey wiederverwenden.
Die E-Mail ist dein Sicherheitsnetz
LeadJourney matcht Events auch über die E-Mail. Sende die E-Mail des Users immer mit jedem Event, damit die Stufen – selbst wenn nie eine Click-ID erfasst wurde (organischer oder direkter Sign-up) oder sie verloren ging – auf dieselbe Person zusammengeführt werden und später zugeordnet werden können, falls die Person vorher auf einen getrackten Link geklickt hat. Siehe Wie Attribution funktioniert.
Bevor du startest
Hol dir deine Conversion-API-Zugangsdaten unter Settings → Events → Conversion API:
- Deine Postback-URL – sieht aus wie
https://api.leadjourney.io/api/v1/postback/<deine-id>. - Dein Postback-Secret – beginnt mit
pb_…. Behandle ihn wie ein Passwort; er bleibt auf deinem Server und wird nie im Browser sichtbar.
Lege dann im Events Manager die Events an, die du nutzen
willst. WonClient gibt es in jedem Workspace standardmäßig; füge die In-Product-Events hinzu:
| Event | Empfohlener Modus | Hinweise |
|---|---|---|
FreeSignup | Ignore duplicate postbacks | Eins pro User – verwirft versehentliche Wiederholungen. |
TrialStarted | Ignore duplicate postbacks | Ein User startet einen Trial einmal. |
TrialEnded | Ignore duplicate postbacks | Schalte Send back to Ads aus – es ist ein Drop-off-Signal, keine Conversion. |
WonClient | Edit conversion with new data | Schalte Include Revenue Value ein. |
Warum „Ignore duplicate postbacks“ hier wichtig ist
Lifecycle-Events werden oft aus erneut zugestellten Webhooks oder einem Cron, der User neu durchgeht gesendet – dasselbe Event kann also mehrfach für dieselbe Person feuern. Diese auf Ignore duplicate postbacks zu setzen hält deine Funnel-Zahlen ehrlich. Siehe die Modus-Tabelle in Events & der Events Manager.
Schritt für Schritt
Die Click-ID beim Sign-up erfassen und speichern
Wenn ein neues Konto erstellt wird, liest du die Click-ID aus dem Browser und speicherst sie am User. Mach das einmal – davon hängt jede spätere Stufe ab.
// In deinem Sign-up-Handler, im Moment, in dem das Konto erstellt wird
const clickId = localStorage.getItem("clickId") || "";
// Am User-Datensatz speichern (Pseudo-Code – nutze dein eigenes ORM / deine DB)
await db.users.update(newUser.id, { lj_click_id: clickId });SSO-Sign-ups (Google / Microsoft)
Bei Google- oder Microsoft-Registrierung wird das Konto in einem Callback erstellt, wo
localStorage eventuell weg ist. Reiche die Click-ID über den OAuth-state-Parameter durch und
speichere sie genauso – das komplette Muster steht in Sign-ups in deiner App tracken (inkl.
Google & Microsoft
SSO).
Eine Server-Route hinzufügen, die jedes Lifecycle-Event sendet
Diese eine Route leitet ein Event an LeadJourney weiter und hält deinen Postback-Secret sicher auf dem Server. Das Beispiel ist eine Next.js-App-Router-Route; jedes Backend (Express, eine Serverless-Funktion) funktioniert genauso.
// app/api/lj-event/route.js
export async function POST(req) {
const d = await req.json();
await fetch(
"https://api.leadjourney.io/api/v1/postback/YOUR-POSTBACK-ID",
{
method: "POST",
headers: {
"Authorization": "Bearer YOUR_POSTBACK_SECRET",
"Content-Type": "application/json",
},
body: JSON.stringify({
type: d.type, // "FreeSignup" | "TrialStarted" | "TrialEnded" | "WonClient"
click_id: d.click_id, // der Wert, den du beim Sign-up gespeichert hast
email: d.email, // immer mitschicken – der dauerhafte Match-Schlüssel
firstname: d.firstname,
lastname: d.lastname,
tag: d.tag, // optionaler Quellen-/Kampagnen-Tag, ein festes Wort genügt
revenue: d.revenue, // nur für WonClient – sonst weglassen
}),
}
);
return Response.json({ ok: true });
}Nutze deine eigene URL und deinen eigenen Secret
Ersetze YOUR-POSTBACK-ID und YOUR_POSTBACK_SECRET durch die Postback-URL und den
Postback-Secret aus deinen eigenen Settings → Events → Conversion API. Schreibe den
Secret nie in Client-Code und committe ihn nie in ein öffentliches Repo.
Jede In-Product-Stufe feuern
Bei jedem Lifecycle-Wechsel rufst du die Route mit dem passenden type auf. Weil du die Click-ID
beim Sign-up gespeichert hast, kann jeder Aufruf sie mitschicken – ganz ohne Browser.
Feuere einmal, direkt nach der Konto-Erstellung (an derselben Stelle, wo du die Click-ID
gespeichert hast). Wenn du die Click-ID hier direkt aus localStorage liest, brauchst du sie noch
nicht mal aus der DB.
await fetch("/api/lj-event", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
type: "FreeSignup",
click_id: localStorage.getItem("clickId"), // oder der gerade gespeicherte Wert
email: newUser.email,
firstname: newUser.firstName,
lastname: newUser.lastName,
tag: "saas_signup",
}),
});Feuere, wenn der Trial tatsächlich beginnt – das kann beim Sign-up sein (Self-Serve-Trial) oder später (der User hinterlegt eine Karte, wählt einen Plan oder aktiviert einen Trial). Lies die gespeicherte Click-ID:
await sendEvent({
type: "TrialStarted",
click_id: user.lj_click_id, // der Wert, den du beim Sign-up gespeichert hast
email: user.email,
firstname: user.firstName,
lastname: user.lastName,
tag: "saas_trial",
});Sign-up und Trial im selben Moment?
Bei einem klassischen Product-Led-Trial gilt Free-Sign-up = Trial gestartet. Du kannst entweder beide Events senden (sauberer Funnel) oder nur eins – sende die Stufe, über die dein Team tatsächlich berichtet.
Hinter dieser Stufe steckt meist keine User-Aktion – es ist ein Datum, das vergeht. Feuere sie aus deiner Billing-Logik, wenn du den Trial auf abgelaufen setzt, oder aus einem nächtlichen Cron, der Trials findet, deren Enddatum in der Vergangenheit liegt:
// Pseudo-Code für einen täglichen Job
const expired = await db.trials.findExpiredSince(yesterday);
for (const t of expired) {
await sendEvent({
type: "TrialEnded",
click_id: t.user.lj_click_id,
email: t.user.email,
tag: "saas_trial_end",
});
}Trial beendet ist ein Signal, kein Verkauf
Trial beendet ist vor allem nützlich, um zu sehen, wo der Funnel leckt – welche Kampagnen Trials bringen, die nie konvertieren. Lass Send back to Ads für dieses Event aus, damit du nicht darauf optimierst.
Für diese Stufe brauchst du keinen Code – überlass das der Stripe-
Integration. Mappe ein Stripe-Event Zahlung erfolgreich
(oder Subscription erstellt) auf deine WonClient-Conversion, und der Umsatz wird automatisch
zugeordnet. Das Tracking-Script versieht deine Stripe Payment Links sogar mit der Click-ID, sodass
Zahlungen der richtigen Kampagne zugeordnet werden – ohne dass du etwas von Hand mappst.
Wenn du außerhalb von Stripe abrechnest, sende es stattdessen über dieselbe Route – mit
revenue:
await sendEvent({
type: "WonClient",
click_id: user.lj_click_id,
email: user.email,
revenue: 49, // der gezahlte Betrag, in deiner Workspace-Währung
tag: "saas_purchase",
});Demo-Calls (sales-assisted SaaS)
Wenn Interessenten vor dem Sign-up eine Demo buchen, tracke das als BookedCall. Die meisten
Scheduler (Calendly, SavvyCal, …) können bei einer Buchung einen Webhook posten – richte ihn auf
eine kleine Route wie oben und sende type: "BookedCall" mit der E-Mail des Buchenden. Um den
Call der richtigen Anzeige zuzuordnen, reiche die Click-ID in den Buchungslink durch (als verstecktes
Feld / UTM, das der Scheduler zurückgibt) oder verlass dich auf E-Mail-Matching. Siehe den
Sales Call Funnel und Events & der Events
Manager für das komplette Muster.
Wiederkehrender Umsatz (MRR) ohne Doppelzählung
Ein Abo zahlt jeden Monat – entscheide einmal, wie das auftauchen soll:
- Eine Conversion, aktuell gehalten – setze
WonClientauf Edit conversion with new data, sodass die letzte Zahlung dieselbe Conversion aktualisiert (gut für „aktueller MRR / Planwert"). - Jede Zahlung als neuer Umsatz – setze es auf Keep original data, increment payout, sodass
jede Rechnung obendrauf addiert wird (gut für „insgesamt eingenommen"). Dieser Modus gilt nur für
WonClient. - Refunds und Churn herausrechnen – mappe einen Stripe-Refund auf eine Conversion mit
negativem
revenue, oder sende einChurned-Event, um zu sehen, welche Kampagnen Kunden bringen, die kündigen.
Siehe die Modus-Tabelle in Events & der Events Manager.
Prüfen, ob es funktioniert
- Öffne deine App über einen getrackten Link (einen mit
lj_*-Parametern, z. B. eine Test- Anzeigen-URL oder ein mit dem UTM Builder gebauter Link), damit eine Click-ID gespeichert wird. - Geh den Funnel mit einem Testkonto durch: registrieren, Trial starten, Trial beenden auslösen (oder auf den Cron warten) und einen Test-Kauf in Stripe machen.
- Unter Settings → Events steigt der Total-Zähler jedes Events und sein Status wechselt auf Active.
- Um einen einzelnen Aufruf zu prüfen (Statuscode, Payload, ob die Click-ID oder E-Mail gematcht hat), öffne das API-Postbacks-Log.
Keine Click-ID? Trotzdem das Event senden
Ein User, der organisch oder direkt kam, hat keine Click-ID – click_id ist dann leer, und das
ist okay. Sende jede Stufe trotzdem mit der E-Mail: LeadJourney matcht auch über die E-Mail,
das Konto wird also erfasst und kann später zugeordnet werden, falls die Person vorher auf einen
getrackten Link geklickt hat. Blockiere ein Lifecycle-Event nie nur, weil die Click-ID fehlt.
FAQ
Verwandte Artikel
Sign-ups in deiner App tracken (inkl. SSO)
Der Deep Dive zum Sign-up-Event, inklusive Google- & Microsoft-SSO.
Stripe verbinden
Käufe und Abo-Umsatz automatisch reinholen – ohne Code.
Events & der Events Manager
Lege deine Lifecycle-Events an, wähle Modi und hol dir die Conversion-API-Zugangsdaten.
API-Postbacks-Log
Prüfe jeden Event-Aufruf und ob er einen Klick oder eine E-Mail gematcht hat.
Affiliate Landing Pages
Lege die Seiten an, auf die du Affiliate-Traffic schickst – Money Pages und Pre-Lander – verbinde sie mit einer Campaign und split-teste Varianten gewichtet, um die Seite mit der höchsten Conversion zu finden.
Deinen iGaming-Funnel tracken (Registrierung → FTD → NGR)
Der komplette Guide für Sportwetten-Anbieter und Casinos – ordne jeden Schritt der Ad, dem Affiliate oder dem Post zu, der ihn gebracht hat. Tracke die Registrierung, dann den First-Time Deposit (FTD), Redeposits und Net Gaming Revenue. Der FTD und alles danach feuern serverseitig aus deiner Player-Plattform oder deinem CRM. Die Schlüsselidee: erfasse die Click-ID einmal bei der Registrierung und spiele sie bei jedem späteren Deposit-Event wieder ab.