# Personen & Kohorten

Events beschreiben Vorgänge. Personen beschreiben, wer sie ausgelöst hat —
und Kohorten sind Gruppen von Personen nach Regeln, die du selbst festlegst:
„Stammkunden“, „Warenkorbabbrecher der letzten 7 Tage“, „B2B-Kunden ohne Kauf“.

Eine Kohorte lässt sich als Kundenliste exportieren und bei Meta (Custom
Audience) oder Google Ads (Customer Match) hochladen. Alles, was die Datei
enthält, sind Hashes und Kundennummern — Klartext kennt Trackdolphin nicht.

## Was eine Person ist

Eine Person je Shop ist der erste bekannte Wert dieser Reihe:

1. `external_id` — die Kundennummer deines Shops (Klartext, höchstens 128 Zeichen),
2. sonst `em` — die SHA-256-gehashte E-Mail,
3. sonst `visitor_id` — die zufällige Besucherkennung aus dem SDK-Cookie.

Solange nur die Besucherkennung bekannt ist, zählt die Person als „anonym“.
Sobald ein Event Kundennummer oder E-Mail-Hash mitbringt, wird sie unter
diesem Schlüssel geführt. Die anonymen Besuche davor erscheinen in ihrer
Reise, weil die Besucherkennung dieselbe bleibt.

Es gibt keinen eigenen Personenspeicher. Erster und letzter Kontakt, Anzahl
Käufe, Umsatz und Eigenschaften entstehen bei jeder Abfrage aus den Events.
Löschst du die Events einer Person, ist auch die Person weg — es gibt keine
zweite Kopie, die jemand vergessen könnte.

## identify() im SDK

Das SDK kennt dafür einen eigenen Aufruf:

```js
td.identify({
  email: "anna@example.com",   // wird vor dem Senden gehasht
  phone: "+49 171 2345678",    // ebenfalls — in beiden Telefon-Formaten
  externalId: "K-10042",       // die Kundennummer deines Shops, Klartext
  traits: { kundengruppe: "b2b", newsletter: true, bestellungen: 3 },
});
```

Das erzeugt ein Event vom Typ `identify`. Es ist weder Kauf noch Lead und
geht **an keine Werbeplattform** — auch nicht an GA4. Es sagt nur: dieser
Besucher ist diese Person, und das weiß der Shop über sie.

Ab dann setzt das SDK `external_id` automatisch an alle weiteren Events des
Browsers, sodass Käufe und Seitenaufrufe derselben Person zugeordnet werden.
Serverseitig gibst du `external_id` selbst mit.

**Traits** sind flache Angaben: Text, Zahl oder Wahrheitswert, höchstens
20 Schlüssel, Werte höchstens 256 Zeichen. Verschachtelte Objekte werden
abgewiesen. Was der Shop zuletzt gemeldet hat, gilt — ein neuer
`identify()`-Aufruf ersetzt den Stand, er ergänzt ihn nicht.

> **Wichtig:** Der Collector nimmt `em` und `ph` nur als SHA-256-Hash an.
> Schickst du eine Klartext-E-Mail direkt an die API, antwortet er mit 400.
> Das SDK hasht für dich; siehe [Match-Qualität](/docs/matching.md).

## Regeln

Alle Regeln einer Kohorte sind optional und gelten zusammen (UND). Eine
Kohorte ohne Regel sind alle Personen des Shops.

| Regel | Bedeutung |
|---|---|
| `purchases_min` | mindestens so viele Käufe |
| `purchases_max` | höchstens so viele Käufe — `0` heißt „noch nie gekauft“ |
| `revenue_min` | Mindestumsatz über alle Käufe (Shop-Währung) |
| `last_seen_days` | zuletzt aktiv innerhalb der letzten n Tage |
| `first_seen_days` | zum ersten Mal gesehen innerhalb der letzten n Tage (Neukunden) |
| `event_types` | hat mindestens eines dieser Events ausgelöst, z. B. `["add_to_cart"]` |
| `traits` | Eigenschaften aus `identify()`, die genau so gesetzt sein müssen |

Beispiele:

- Stammkunden: `{ "purchases_min": 2 }`
- Warenkorbabbrecher der Woche: `{ "event_types": ["add_to_cart"], "purchases_max": 0, "last_seen_days": 7 }`
- B2B ohne Kauf: `{ "traits": { "kundengruppe": "b2b" }, "purchases_max": 0 }`

Gezählt werden ausschließlich Events der Umgebung `production` — ein
Staging-Testkauf macht niemanden zum Stammkunden.

## Über die API

```
GET    /api/shops/{shopId}/persons?limit=50&cursor=…
GET    /api/shops/{shopId}/persons/{key}
GET    /api/shops/{shopId}/cohorts
POST   /api/shops/{shopId}/cohorts            { "name": "…", "rules": { … } }
PUT    /api/shops/{shopId}/cohorts/{id}
DELETE /api/shops/{shopId}/cohorts/{id}
GET    /api/shops/{shopId}/cohorts/{id}/preview
GET    /api/shops/{shopId}/cohorts/{id}/export?format=csv
```

Authentifizierung wie überall: ein API-Schlüssel als Bearer-Token, siehe
[API](/docs/api.md). Im [MCP-Server](/docs/mcp.md) stehen dieselben Fähigkeiten
als Werkzeuge `persons_list`, `person_journey`, `cohorts_list`,
`cohort_create`, `cohort_preview` und `cohort_export` zur Verfügung — ein
Agent kann also fragen: „Wie viele Kunden haben in den letzten 30 Tagen
zweimal gekauft?“ und die Kohorte gleich anlegen.

## Export für Meta und Google

Der Export liefert vier Spalten:

| Spalte | Inhalt | Für |
|---|---|---|
| `em` | SHA-256 der E-Mail, Kleinbuchstaben | Meta, Google |
| `ph` | SHA-256 der Telefonnummer in E.164 **ohne** Plus | Meta |
| `ph_e164` | SHA-256 der Telefonnummer in E.164 **mit** Plus | Google Ads |
| `external_id` | Kundennummer, Klartext | Meta (`extern_id`) |

Beim Hochladen ordnest du die Spalten den Feldern der Plattform zu und
markierst sie als bereits gehasht. Personen ohne eines dieser Merkmale — also
rein anonyme Besucher — fehlen in der Datei: Eine Zeile aus leeren Feldern
könnte keine Plattform zuordnen.

Eine Datei enthält höchstens 100.000 Zeilen. Größere Kohorten teilst du über
die Regeln auf — Kundenlisten werden bei beiden Plattformen ohnehin in
Stücken hochgeladen.

## Datenschutz

- **Nur Hashes.** E-Mail und Telefon erreichen Trackdolphin ausschließlich
  als SHA-256. Es gibt keinen Weg zurück zum Klartext — auch nicht für uns.
  Die gekürzte Darstellung in der Personenliste dient dem Wiedererkennen,
  nicht dem Entschlüsseln.
- **Traits sind Kundenangaben.** Was du in `identify()` mitgibst, ist deine
  Entscheidung und deine Verantwortung: Es sollte nur enthalten, was du für
  Zielgruppen tatsächlich brauchst. Keine Gesundheitsdaten, keine
  Freitextnotizen, keine Adressen.
- **Kohorten speichern Regeln, keine Mitglieder.** Es gibt keine
  Mitgliederliste, die veralten oder vergessen werden könnte.
- **Löschung.** Löschst du die Events einer Person (siehe
  [Datenschutz](/docs/privacy.md)), verschwindet sie aus jeder Kohorte und
  jedem künftigen Export. Bereits hochgeladene Kundenlisten löschst du bei
  der jeweiligen Plattform.
- **Einwilligung.** Kundenlisten bei Werbeplattformen setzen eine Rechtsgrundlage
  voraus — in der Regel die Einwilligung der Person in Werbung. Trackdolphin
  prüft das nicht für dich; der Export ist ein Werkzeug, kein Freibrief.
