Tutorial: Die TikTok Events API für Onlineshops implementieren

Inhalt
Ein TikTok-Pixel allein meldet weniger Käufe, als ein Shop tatsächlich abwickelt. Blocker, der Tracking-Schutz im Browser und Besucher, die den Tab schließen, bevor die Bestätigungsseite fertig gerendert ist, entfernen Events, die stattgefunden haben. Dieselben Käufe ein zweites Mal vom Server zu senden schließt diese Lücke – und erzeugt ein neues Problem, denn TikTok erhält nun zwei Meldungen über einen Kauf.
Aufgelöst wird das über eine gemeinsame event_id: ein Wert, je Kauf einmal erzeugt und unverändert auf beiden Wegen gesendet. Die folgende Anleitung baut diese Architektur vollständig für einen Shop auf – das Browser-Event, die Kennungen, die der Server von sich aus nicht sieht, die Server-Anfrage und die Prüfung, ob die Deduplizierung tatsächlich greift.

Schritt 1: Die Event-ID einmal erzeugen
Die event_id muss an einer Stelle erzeugt und wiederverwendet werden – sie unabhängig auf beiden Seiten zu erzeugen scheidet damit aus, denn zwei Zufallswerte beschreiben aus Sicht von TikTok zwei getrennte Conversions. Für einen Shop ist die Bestellnummer die naheliegende Quelle: Sie existiert im Moment des Kaufs bereits, ist stabil und steht sowohl der Bestätigungsseite als auch dem Backend zur Verfügung.
Ein Präfix hält Eventtypen auseinander, denn eine einzelne Bestellung kann mehr als ein getracktes Event erzeugen. Eine event_id purchase-10432 für den Kauf und checkout-10432 für den Checkout-Start hält jedes Paar bei seinem eigenen Gegenstück. Als Nebeneffekt erzeugt ein Besucher, der die Bestätigungsseite neu lädt, dieselbe event_id erneut und wird dedupliziert statt doppelt gezählt.
Schritt 2: Das Browser-Event
Im TikTok-Pixel reist die Kennung im dritten Argument von ttq.track, getrennt von den Event-Eigenschaften:
ttq.track('CompletePayment', {
contents: [
{ content_id: 'SKU-8891', content_type: 'product', quantity: 1, price: 149.00 }
],
value: 149.00,
currency: 'PLN'
}, {
event_id: 'purchase-10432'
});
Auch der Eventname muss auf beiden Seiten übereinstimmen. TikTok paart die beiden Meldungen über die Kombination aus Eventname und event_id; CompletePayment im Browser und Purchase auf dem Server sind also zwei verschiedene Events, die nie gegeneinander deduplizieren – gleich wie sorgfältig die Kennung abgeglichen wird.
Schritt 3: Die Kennungen erfassen, die der Server nicht sieht
Zwei Werte existieren nur im Browser. ttclid kommt als URL-Parameter an, wenn ein Besucher über eine TikTok-Anzeige landet, und _ttp ist das First-Party-Cookie, das das Pixel setzt. Beide verbessern die Zuordnung der Server-Anfrage, und beide müssen beim Landen gesichert werden – die Bestätigungsseite liegt meist mehrere Navigationen später, und der URL-Parameter ist dann längst weg:
// Beim Landen: Klick-ID fuer den restlichen Besuch sichern
const ttclid = new URLSearchParams(window.location.search).get('ttclid');
if (ttclid) {
document.cookie = 'shop_ttclid=' + encodeURIComponent(ttclid) +
';max-age=2592000;path=/;secure;samesite=Lax';
}
Im Checkout werden beide Werte aus den Cookies gelesen und am Bestelldatensatz gespeichert, damit das Backend sie beim Bau des Server-Events zur Hand hat. IP-Adresse und User Agent des Besuchers stammen aus der ursprünglichen HTTP-Anfrage und gehören in denselben Datensatz – eine später vom Server gesendete Anfrage hat weder das eine noch das andere von sich aus.
Schritt 4: Die Server-Anfrage
Die Events API nimmt einen POST an den event/track/-Endpunkt der TikTok Business API entgegen, authentifiziert über ein Access Token aus dem TikTok Events Manager und über event_source_id an ein Pixel adressiert. Identifizierende Felder werden normalisiert und dann per SHA-256 gehasht; nichts Identifizierendes verlässt den Server im Klartext:
{
"event_source": "web",
"event_source_id": "PIXEL_CODE",
"data": [{
"event": "CompletePayment",
"event_time": 1789000000,
"event_id": "purchase-10432",
"user": {
"email": "1f3a...",
"phone": "9c22...",
"external_id": "7b41...",
"ttclid": "E.C.P.xxxxx",
"ttp": "2ABCDe...",
"ip": "203.0.113.7",
"user_agent": "Mozilla/5.0 ..."
},
"properties": {
"contents": [
{ "content_id": "SKU-8891", "content_type": "product", "quantity": 1, "price": 149.00 }
],
"currency": "PLN",
"value": 149.00
},
"page": { "url": "https://shop.example.com/danke" }
}]
}
Die Normalisierung vor dem Hashen entscheidet, ob die Kennungen überhaupt etwas treffen: E-Mail getrimmt und kleingeschrieben, Telefonnummer auf Ziffern im internationalen E.164-Format reduziert. Derselbe Wert, einmal aus Anna@Example.com und einmal aus anna@example.com gehasht, ergibt zwei unabhängige Digests, von denen nur einer passen kann. event_time ist ein Unix-Zeitstempel in Sekunden, und die Anfrage gehört unmittelbar hinter die Bestellbestätigung statt in einen nächtlichen Stapel. TikTok weist zu spät eintreffende Events ab, und ein Batch-Job merkt das erst, wenn das Zeitfenster bereits zu ist.
Schritt 5: Prüfen, ob die Deduplizierung greift
Das Feld test_event_code aus dem Reiter für Testereignisse im Events Manager leitet eine Anfrage in die Testansicht, ohne das Reporting zu berühren – genug, um zu bestätigen, dass die Nutzlast angenommen und die gehashten Felder erkannt werden.
Die Deduplizierung selbst zeigt sich erst in Echtdaten. Im Events Manager weist ein Event, das beide Kanäle erhält, seine Browser- und Serverzahlen getrennt aus; die Differenz zwischen deren Summe und dem deduplizierten Gesamtwert ist der Beleg, dass die Paarung funktioniert. Ein Gesamtwert, der genau der Summe beider Kanäle entspricht, bedeutet, dass nichts zusammengeführt wird – meist ein abweichender Eventname oder eine je Seite getrennt erzeugte event_id. Der Abgleich der deduplizierten Zahl gegen die tatsächlichen Bestellungen im Shop-Backend ist die Prüfung, auf die es ankommt; TikToks eigene Zahlen können keinen Kauf zeigen, den kein Kanal gemeldet hat.
Innerhalb der Leitplanken bleiben
Beide Kanäle gehören hinter dasselbe Werbe-Consent-Signal. Eine serverseitige Anfrage ist nicht deshalb von der Einwilligung ausgenommen, weil sie aus einem Backend stammt: Sie trägt dieselben Kundendaten, und die Rechtsgrundlage dafür ist die im Browser eingeholte – der Consent-Status muss also am Auftrag gespeichert und vor dem Bau der Anfrage geprüft werden. Rohe E-Mail-Adressen und Telefonnummern existieren nur so lange, bis sie normalisiert und gehasht sind, und das Access Token gehört in einen Secrets-Speicher statt in den Anwendungscode, denn es berechtigt zum Schreiben von Conversions in das Werbekonto.
Fragen und Antworten
Warum erreicht die deduplizierte Zahl auch mit der Events API nicht die Zahl der Bestellungen?
Weil die Events API nur eine von zwei Lücken schließt. Der Server holt Käufe zurück, die Blocker, Tracking-Schutz oder ein geschlossener Tab dem Pixel genommen haben. Käufe ohne Werbe-Einwilligung sendet er dagegen zu Recht nicht, denn beide Kanäle stehen hinter demselben Consent-Signal. Diese Bestellungen fehlen bei TikTok dauerhaft, und das ist kein Messfehler.
Der Abgleich aus Schritt 5 wird deshalb erst aussagekräftig, wenn er auf beiden Seiten dieselbe Grundgesamtheit betrachtet: die deduplizierte Zahl gegen die Bestellungen, an denen der gespeicherte Consent-Status eine Einwilligung ausweist. Bleibt dort eine Lücke, liegt sie in der eigenen Kette, etwa bei einem Server-Event, das nie gebaut wurde, einer abgewiesenen Anfrage oder einem Event, das zu spät ankam.
Hilfreich ist es, die Antwort der Events API am Auftrag zu speichern. Dann lässt sich jede Bestellung einem von vier Zuständen zuordnen: nicht gesendet mangels Einwilligung, trotz Einwilligung nicht gesendet, gesendet und abgewiesen, gesendet und angenommen. Nur der letzte gehört in die Zahl, die TikTok zeigen sollte, und jeder der beiden mittleren ist ein Fehler, der sich beheben lässt.
Hält das Cookie shop_ttclid in jedem Browser die vollen 30 Tage?
Nein. max-age=2592000 entspricht 30 Tagen, doch Safari lässt Cookies, die ein Skript über document.cookie setzt, nach höchstens sieben Tagen ablaufen, unter Umständen schon nach 24 Stunden, wenn der Besuch über einen Link mit Parametern von einer als Tracker eingestuften Domain kam. Wer die Klick-ID länger halten will, lässt das Cookie vom Shop-Server selbst per Set-Cookie-Header setzen; _ttp setzt das Pixel ebenfalls per Skript, und für dieses Cookie gilt dieselbe Grenze.
Müssen Wert und Warenkorb im Browser- und im Server-Event übereinstimmen?
Ja. Nach der Deduplizierung zählt nur eine der beiden Meldungen, und weichen value, currency oder contents voneinander ab, hängt der gemeldete Umsatz davon ab, welche es ist. Am sichersten entstehen beide aus demselben Bestelldatensatz, der auch die event_id liefert: Ob brutto oder netto, mit oder ohne Versand, wird einmal entschieden und nicht je Kanal.