B2B-Lead-Attribution: UTM-Parameter ins CRM übertragen

Management Summary
Im Geschäft mit Unternehmen trifft eine Anfrage oft erst nach dem ersten Werbekontakt ein. Kampagnenkennzeichnungen in Weblinks können diesen Zusammenhang ins CRM übertragen, das System zur Verwaltung von Kundenkontakten und Verkaufschancen. Mit gültiger Einwilligung helfen der erste und jüngste beobachtete Kontakt, spätere Anfragen einzuordnen. Das unterstützt Marketing und Vertrieb, zeigt aber nur einen Ausschnitt des Entscheidungswegs und beweist nicht, dass eine Anzeige den Verkauf verursacht hat.
Inhalt
B2B bezeichnet Geschäfte zwischen Unternehmen. Ein Lead ist ein Kontakt mit möglichem Kaufinteresse. Dieser Kontakt kann über eine Anzeige entstehen; das Formular wird möglicherweise erst nach mehreren Besuchen abgesendet. Die Kampagnenparameter der Einstiegsseite stehen dann häufig nicht mehr in der aktuellen URL. Eine eigene Speicherung kann diese Werte an ein Formular und anschließend an das CRM übergeben. Ein CRM verwaltet Kundenkontakte und Verkaufschancen. Das ergänzt die Formularintegration; GA4 verliert eine bereits erfasste Kampagnenquelle nicht automatisch beim nächsten internen Seitenwechsel.
1. Was First Touch und Last Touch hier bedeuten
Das folgende Beispiel speichert den frühesten und den jüngsten beobachteten Besuch mit gültigen UTM-Parametern oder unterstützten Klick-IDs. Besuche ohne solche Parameter verändern diese Einträge nicht. First Touch bedeutet deshalb den frühesten noch verfügbaren Kampagnenkontakt dieses Browsers, Last Touch den jüngsten erfassten Kampagnenkontakt. Attribution ordnet Kontakte oder Verkäufe Marketingkanälen zu. Eine vollständige Kundenreise oder eine kanalübergreifende Attribution entsteht aus diesen zwei Einträgen nicht.
UTM-Parameter sind Kampagnenkennzeichnungen in einer URL, etwa utm_source=newsletter. Die fünf UTM-Felder beschreiben Quelle, Medium, Kampagne, Suchbegriff und Anzeigeninhalt. Zusätzlich werden gclid und li_fat_id getrennt mit einem Erfassungszeitpunkt gespeichert. So bleibt beispielsweise die Google-Klick-ID erhalten, wenn später eine E-Mail-Kampagne den Last Touch aktualisiert. Andere Kennungen wie wbraid und gbraid benötigen eine gesondert geprüfte Integration.
2. Speicherung mit Einwilligung und Ablaufdatum

localStorage ist ein Browserspeicher, den eine Website mit JavaScript lesen und beschreiben kann. Eine Consent Management Platform (CMP) verwaltet Einwilligungsentscheidungen. Marketingattribution im localStorage benötigt in Deutschland grundsätzlich eine Einwilligung nach § 25 TDDDG. localStorage ist keine Ausnahme vom Schutz der Endeinrichtung. Der Zugriff bleibt bis zu einer bestätigten Freigabe gesperrt. Bei Widerruf muss die CMP-Integration die weitere Nutzung stoppen, den gespeicherten Datensatz löschen und bereits befüllte Attributionsfelder leeren.
Das ES-Modul verwendet ausschließlich localStorage. Die beispielhafte Frist von 30 Tagen gilt pro Kontakt beziehungsweise Klick-ID ab Erfassung und ist keine allgemeine Aufbewahrungsempfehlung. Abgelaufene Werte werden nicht mehr ausgegeben; physisch bereinigt wird der Datensatz beim nächsten capture-Aufruf. Browserlöschung, Tracking-Schutz und Speicherfehler können die tatsächliche Verfügbarkeit verkürzen. Ein Origin besteht aus Protokoll, Hostname und Port einer Webadresse. Gleicher Origin ist erforderlich: Subdomains und andere Browser teilen diesen Speicher nicht.
export function attributionStore({ consent, storage, now = Date.now }) {
const key = 'b2b_attribution_v2';
const ttl = 30 * 86400000;
const utm = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
const ids = ['gclid', 'li_fat_id'];
const valid = value => typeof value === 'string' && value.trim().length > 0
&& value.length <= 500 && !/[\u0000-\u001f\u007f]/.test(value);
function permitted() { try { return consent() === true; } catch { return false; } }
function fresh(record, time) {
return record && Number.isFinite(record.at) && record.at <= time
&& time - record.at < ttl && record.values && typeof record.values === 'object';
}
function read(time) {
const data = JSON.parse(storage().getItem(key) || '{}');
const result = { first: null, last: null, ids: {} };
for (const name of ['first', 'last']) {
if (fresh(data?.[name], time)) {
const values = Object.fromEntries([...utm, ...ids]
.filter(k => valid(data[name].values[k]))
.map(k => [k, data[name].values[k]]));
if (Object.keys(values).length) result[name] = { at: data[name].at, values };
}
}
for (const name of ids) {
const item = data?.ids?.[name];
if (fresh(item, time) && valid(item.values[name])) result.ids[name] = item;
}
return result;
}
return {
capture(search) {
if (!permitted()) return false;
try {
const time = now();
let data;
try { data = read(time); } catch { data = { first: null, last: null, ids: {} }; }
const params = new URLSearchParams(search);
const values = Object.fromEntries([...utm, ...ids]
.filter(k => params.getAll(k).length === 1 && valid(params.get(k)))
.map(k => [k, params.get(k).trim()]));
if (Object.keys(values).length) {
const touch = { at: time, values };
data.first ||= touch;
data.last = touch;
for (const name of ids) if (values[name]) {
data.ids[name] = { at: time, values: { [name]: values[name] } };
}
}
storage().setItem(key, JSON.stringify(data));
return true;
} catch { return false; }
},
fields() {
if (!permitted()) return {};
try {
const data = read(now());
const fields = {};
for (const side of ['first', 'last']) {
for (const name of utm) fields[side + '_' + name] = data[side]?.values[name] || '';
fields[side + '_captured_at'] = data[side] ? new Date(data[side].at).toISOString() : '';
}
for (const name of ids) {
fields[name] = data.ids[name]?.values[name] || '';
fields[name + '_captured_at'] = data.ids[name] ? new Date(data.ids[name].at).toISOString() : '';
}
return fields;
} catch { return {}; }
},
clear() { try { storage().removeItem(key); } catch {} }
};
}
Einbindung als ES-Modul, beispielsweise aus /assets/utm-store.mjs. Ein unverändertes Custom-HTML-Tag kann export/import nicht als klassisches JavaScript ausführen. Die CMP-Anbindung muss den tatsächlichen, aktuellen Einwilligungsstatus liefern; das folgende sichere Ausgangsbeispiel gibt bewusst keine Speicherung frei.
import { attributionStore } from '/assets/utm-store.mjs';
const attribution = attributionStore({
consent: () => false, // Replace with the verified CMP adapter.
storage: () => window.localStorage
});
// After consent is confirmed, and on relevant SPA navigation:
attribution.capture(window.location.search);
// In the CMP withdrawal handler:
// attribution.clear();
capture muss nach der Freigabe erneut aufgerufen werden, solange die Kampagnenparameter noch verfügbar sind. Ein Aufruf nur vor der Einwilligung erfasst den Besuch nicht nachträglich. Single-Page-Anwendungen ändern den angezeigten Inhalt, ohne ein vollständiges neues Dokument zu laden. Die Erfassung erfolgt bei den relevanten Änderungen der URL während der Navigation. Für den Parameterzugriff ist kein DOMContentLoaded-Ereignis erforderlich. Doppelte, leere, überlange oder Steuerzeichen enthaltende Werte werden verworfen. Ein erneuter Aufruf mit derselben markierten URL zählt als erneute Beobachtung; Erfassungszeitpunkte sind keine verifizierten Werbeklickzeitpunkte.
3. Formularfelder gezielt zuordnen
fields() liefert benannte Felder ohne verschachtelte Objekte, etwa first_utm_source, last_utm_campaign, gclid und gclid_captured_at. Fehlende Angaben bleiben leer. Ein leeres Feld beweist keinen direkten Besuch. Auch eine vorhandene gclid allein befüllt die UTM-Felder nicht automatisch. Eine daraus abgeleitete Kanalkategorie benötigt eine ausdrücklich dokumentierte Regel und bleibt von den Rohwerten getrennt.
Ein verstecktes Feld wird mit dem Formular übertragen, erscheint aber nicht als Eingabefeld. Für ein eigenes HTML-Formular kann jedes vorbereitete versteckte Feld ein data-attribution-key mit dem passenden Schlüssel erhalten. Die Funktion aktualisiert ausschließlich so markierte Felder und leert alte Werte, falls keine Freigabe mehr vorliegt. Der Formular-Submit-Handler muss die Funktion vor dem Erstellen der Formulardaten aufrufen; asynchrone Bibliotheken benötigen den jeweiligen dokumentierten Hook.
function fillAttribution(form) {
const values = attribution.fields();
for (const field of form.querySelectorAll('input[type=hidden][data-attribution-key]')) {
field.value = values[field.dataset.attributionKey] || '';
}
}
// Example field in a custom form:
// <input type="hidden" name="campaign_source" data-attribution-key="first_utm_source">
Gravity Forms bietet ab Version 2.9 das native Ereignis gform/post_render. Das ältere gform_post_render ist ein jQuery-Ereignis und funktioniert nicht durch denselben Namen in document.addEventListener. Ereignis und Feldzuordnung müssen zur eingesetzten Version passen; ein CRM-Feldname ist nicht automatisch der HTML-Name eines Gravity-Forms-Feldes. Aktuelle HubSpot-Formulare bieten eigene Instanzen und setFieldValue. Ein iframe bettet eine andere Seite in die aktuelle Seite ein. Eine allgemeine Suche im HTML-Baum des Hauptdokuments erreicht ein iframe von einem anderen Origin nicht. Auch Contact Form 7 benötigt angelegte Felder und eine passende Übermittlung.
4. Vom Formular zum CRM und zur Offline-Conversion
Das Backend validiert die übermittelten Werte erneut: Hidden Fields und URL-Parameter sind veränderbare Eingaben. E-Mail-Adressen, Namen und andere personenbezogene Inhalte gehören nicht in UTM-Parameter. CRM-Eigenschaften, Formularfelder und API-Felder benötigen eine ausdrückliche Zuordnung; gleiche Anzeigenamen stellen keine Verbindung her.
Für bestehende Kontakte braucht der Import eine Regel für den frühesten bekannten Kontakt, spätere Kampagnen und zusammengeführte Datensätze. Kontakt und Deal werden ausdrücklich verknüpft. Eine Kanalgruppe wie Paid Social (bezahlte Werbung in sozialen Netzwerken) beschreibt die Herkunft. Lead, Opportunity und Closed-Won bezeichnen den Vertriebsstatus: einen möglichen Kundenkontakt, eine konkrete Verkaufschance und einen abgeschlossenen Verkauf. Beide Dimensionen bleiben getrennt.
Eine Offline-Conversion ist ein außerhalb der Website erfasstes Ergebnis, etwa ein im CRM bestätigter Verkauf. Die Dublettenbehandlung verhindert, dass dasselbe Ergebnis mehrfach gezählt wird. Offline-Conversions entstehen erst durch eine eigene Integration mit Conversion-Aktion, tatsächlichem Conversion-Zeitpunkt, passender Kennung, erforderlicher Einwilligung und Dublettenbehandlung. Eine im CRM gespeicherte ID löst keinen Upload aus. Google nennt höchstens 90 Tage seit dem zugehörigen Klick für klassische Offline-Uploads und 63 Tage für Enhanced Conversions for Leads; zusätzlich gilt das konfigurierte Conversion-Fenster. Dieses Zeitfenster legt fest, wie lange nach einer Werbeinteraktion ein Ergebnis noch zugeordnet werden kann. Die Browserfrist ist davon unabhängig. Frühere qualifizierte Vertriebsstufen können eigenständige Conversion-Aktionen bilden, sofern Definition und Werte zum Optimierungsziel passen.
Bei LinkedIn setzt li_fat_id in Landingpage-URLs die aktivierte erweiterte Conversion-Erfassung des Insight Tags im Campaign Manager voraus. Die Conversions API akzeptiert die Kennung als LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID. Die Verfügbarkeit einer Zuordnungskennung beweist noch keine Conversion innerhalb des Attributionsfensters.
5. Prüfung vor dem Einsatz
Ein Testpfad führt von einer Google-Kampagne über eine unmarkierte Unterseite und eine spätere E-Mail-Kampagne zum Formular: First Touch bleibt Google, Last Touch wird E-Mail, die noch gültige gclid bleibt separat erhalten. Weitere Prüfungen umfassen abgelehnte oder widerrufene Einwilligung, abgelaufene und beschädigte Daten, gesperrten Speicher, AJAX-Formulare und die tatsächlichen CRM-Felder. Zehn isolierte JavaScript-Tests prüfen die Speicherlogik; eine produktive CMP-, Formular- oder CRM-Anbindung ist damit noch nicht getestet.
Fragen und Antworten
Bleiben die Daten in Safari 30 Tage erhalten?
Das ist nicht garantiert. WebKit kann skriptbeschreibbaren Speicher nach sieben Tagen Browsernutzung ohne Interaktion mit der Website löschen. Auch serverseitige Cookies sind kein pauschaler Ausweg: Unter anderem können CNAME- oder IP-basierte Tracking-Konstellationen begrenzt werden. Eine rechtmäßig übermittelte CRM-Kopie hängt nicht mehr vom Browserspeicher ab, benötigt aber weiterhin eigene Aufbewahrungs- und Löschregeln.
Sind First Touch und Last Touch eine Multi-Touch-Analyse?
Zwei Kontaktpunkte bilden keine vollständige Ereignisfolge. Das Beispiel speichert außerdem pro Plattform nur die zuletzt beobachtete unterstützte Klick-ID. Für mehrere Klicks, Gerätewechsel oder Kontaktzusammenführungen ist ein weitergehendes, datenschutzkonformes Datenmodell erforderlich. Die Ausgabe beschreibt beobachtete Kampagnenhinweise und beweist keinen ursächlichen Umsatzbeitrag.