Schnellstart SvelteKit
Für Headless-Shops gibt es kein Plugin — dafür das SDK. Der Weg ist derselbe wie bei den Plugins: Der Browser spricht nur mit deiner Domain, dein Server spricht mit dem Collector. Warum das wichtig ist, steht unter 100 % Abdeckung; hier geht es um die Umsetzung.
npm install @trackdolphin/sdkDu brauchst die Collector-URL aus dem Dashboard unter Einrichtung.
Sie steht in deinem Server-Environment, nicht im Browser — über
$env/static/private bricht der Build, wenn sie fehlt, statt dass der Shop
still nichts sendet:
# .env
TD_ENDPOINT=https://abc123.trdph.com/collect1. Die Proxy-Route
Eine Datei. Sie nimmt Events vom Browser an, ergänzt Besucher-IP und User-Agent, setzt die Besucherkennung als echtes Server-Cookie und reicht alles an den Collector weiter. Fehler landen im Server-Log, nie im Browser.
// src/routes/td/+server.ts
import { TD_ENDPOINT } from "$env/static/private";
import { createCollectProxy } from "@trackdolphin/sdk/proxy";
import type { RequestHandler } from "./$types";
const proxy = createCollectProxy({ endpoint: TD_ENDPOINT });
export const POST: RequestHandler = ({ request }) => proxy(request);
export const OPTIONS = POST;2. SDK starten und Seitenaufrufe melden
init() gehört ins Root-Layout, und weil SvelteKit zwischen Routen keine
neue Seite lädt, kommen Seitenaufrufe aus afterNavigate — das läuft auch
beim ersten Rendern.
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { afterNavigate } from "$app/navigation";
import { init, track } from "@trackdolphin/sdk";
let { children } = $props();
// Relativ: der Browser sieht nur deine Domain. Die Besucherkennung
// vergibt die Route (Set-Cookie) — das SDK liest sie nur.
init({ endpoint: "/td", requireConsent: true });
afterNavigate(() => {
track({ type: "custom", custom_name: "page_view" });
});
</script>
{@render children()}Die Einwilligung aus deinem Cookie-Banner übergibst du mit setConsent() —
als true/false oder granular nach
Consent Mode v2. Mit requireConsent: true
bleiben Events bis dahin in der Warteschlange.
3. Produkt und Warenkorb
<!-- src/routes/produkt/[slug]/+page.svelte -->
<script lang="ts">
import { viewItem, addToCart } from "@trackdolphin/sdk";
let { data } = $props();
const item = { id: data.product.sku, name: data.product.name, price: data.product.price };
// Beim Laden der Seite (und bei jedem Produktwechsel)
$effect(() => {
viewItem([item], item.price);
});
</script>
<button onclick={() => addToCart([item], item.price)}>In den Warenkorb</button>4. Der Kauf: Browser und Server mit derselben event_id
Der verlässliche Kauf kommt vom Server — aus der Bestellung, nicht aus dem
Browser. Damit Meta beide Seiten als eine Conversion erkennt, teilen sich
Browser und Server die event_id. Ableiten, nicht würfeln: order_${id}.
Serverseitig in der Form-Action, die die Bestellung abschließt:
// src/routes/checkout/+page.server.ts
import { TD_ENDPOINT } from "$env/static/private";
import { createClient } from "@trackdolphin/sdk/server";
import { redirect } from "@sveltejs/kit";
import type { Actions } from "./$types";
const td = createClient({ endpoint: TD_ENDPOINT });
export const actions: Actions = {
default: async ({ request }) => {
const form = await request.formData();
const order = await finishOrder(form); // zuerst das Geschäft
try {
await td.purchase({
event_id: `order_${order.id}`,
value: order.total,
currency: "EUR",
email: order.email, // wird hier gehasht, verlässt den Server nie im Klartext
phone: order.phone,
items: order.items.map((i) => ({ id: i.sku, quantity: i.quantity, price: i.price })),
});
} catch (e) {
console.error("Tracking fehlgeschlagen — Bestellung ist gesichert", e);
}
redirect(303, `/danke/${order.id}`);
},
};Und auf der Danke-Seite dieselbe event_id im Browser — sie bringt die
Klick-IDs und die Besucherkennung mit, die nur der Browser kennt:
<!-- src/routes/danke/[id]/+page.svelte -->
<script lang="ts">
import { purchase } from "@trackdolphin/sdk";
let { data } = $props();
$effect(() => {
purchase({ event_id: `order_${data.order.id}`, value: data.order.total, currency: "EUR" });
});
</script>Fällt das Tracking aus, ist die Bestellung trotzdem da. Andersherum kostet ein
Ausfall bei uns dich einen Kunden — das darf nie passieren, deshalb das
try/catch nach dem Geschäft.
5. Prüfen
Ruf eine Produktseite auf. Im Dashboard unter Einrichtung zählt der
Eventzähler innerhalb weniger Sekunden hoch; unter Events siehst du jedes
einzelne mit Empfangskanal und Zustellstatus je Plattform. Der Kauf erscheint
dort einmal — mit beiden Quellen, Browser und Server, unter einer event_id.
Kommt nichts an:
- Der Browser meldet
404auf/td: Die Datei liegt nicht untersrc/routes/td/+server.ts, oder einhandle-Hook fängt die Route ab. - Der Browser bekommt
202statt204: Die Route lebt, der Collector hat abgelehnt — die Ursache steht im Server-Log (meist eine falscheTD_ENDPOINT). requireConsent: true, aber niesetConsent()aufgerufen: Die Events warten in der Warteschlange.
Rückfall, falls die Route einmal nicht antwortet
Mit fallbackEndpoint schickt das SDK das erste Event einer Sitzung per
fetch an die Route und merkt sich die Antwort. Antwortet sie nicht oder mit
5xx, geht der Rest der Sitzung direkt an den Collector — ohne Tarnung, aber
nicht verloren:
init({ endpoint: "/td", fallbackEndpoint: "https://abc123.trdph.com/collect" });Danach
Historie importieren — das zeigt dir in Zahlen, wie viel dein bisheriges Tracking übersehen hat. Alles Weitere zur API steht auf der SDK-Seite.