EINE LISTE IST EINE GESPEICHERTE ZIELGRUPPE, KEINE ADRESSLISTE. Gespeichert werden ausschliesslich die Bedingungen (`conditions`); wer dazugehört, wird bei jedem Lesen und beim Versand neu bestimmt. Eine Liste „Stammgäste der letzten 180 Tage" ist deshalb morgen eine andere Menge als heute — und genau das ist gewollt. `cachedCount` und `countedAt` sind die letzte Zählung für Übersichten; die Wahrheit liefert `GET …/lists/{id}` unter `counts`.
DER BEDINGUNGSBAUM ist dieselbe Form wie im Listen-Editor des Dashboards: eine Gruppe `{ "operator": "AND" | "OR" | "XOR", "conditions": [...] }`, darin Regeln `{ "field", "operator", "value"?, "valueTo"?, "values"?, "reservation"?, "campaign"? }` oder weitere Gruppen. XOR heisst „genau eine der Bedingungen". Höchstens drei Gruppenebenen, 40 Regeln und 20 Einträge je Gruppe. Dazu kommt eine Kostengrenze, weil XOR teuer ist: jeder Zweig trägt alle Bedingungen seiner Gruppe, verschachtelt vervielfacht sich das. Ein zu aufwendiger Baum ist 400 `validation` mit der Meldung „Die Bedingungen sind zu aufwendig: „Genau eine (XOR)" vervielfacht jede Bedingung ihrer Gruppe. …" — 40 Regeln in UND/ODER liegen immer darunter. Zeiträume sind RELATIV (`WITHIN_LAST_DAYS`, `MORE_THAN_DAYS_AGO`, `IN_NEXT_DAYS`) und werden in der Zeitzone des Betriebs gerechnet; Beträge (`totalSpent`) sind ganzzahlige CENT; Länder ISO-Codes (`AT`). Felder mit `ctx.` gibt es nur in Automatisierungen — in einer Liste sind sie 400.
ZWEI FELDER MIT UNSCHÄRFE. `country` nimmt das gespeicherte Land des Gastes; fehlt es, wird es NUR aus einer internationalen Vorwahl (`+43 …`) abgeleitet. Nummern im Ortsformat (`0664 …`) und Vorwahlen, die sich mehrere große Länder teilen (`+1`, `+7`, `+262`), ergeben kein Land — solche Gäste treffen weder `IN` noch `NOT_IN`. Bei `campaign` zählen `NOT_OPENED` und `NOT_CLICKED` nur Empfänger, bei denen gemessen werden durfte; Öffnungen gibt es nur bei E-Mails. Ein Gast ohne Messung ist also weder „geöffnet" noch „nicht geöffnet".
ZAHLEN, KEINE VERSPRECHEN. `counts.total` zählt alle Gäste, die die Bedingungen erfüllen. `counts.email` und `counts.sms` zählen davon die, die über den Kanal erreichbar sind: bestätigte Einwilligung UND Adresse bzw. Nummer. Die Sperrliste (Hard-Bounces, Beschwerden, manuelle Sperren) prüft erst der Versand — die tatsächliche Empfängerzahl kann also darunter liegen.
DIE MITGLIEDERLISTE IST EIN ZWEITER WEG IN DIE GÄSTEKARTEI und läuft deshalb unter denselben Schranken wie `/api/v1/guests`: zusätzlich zu `marketing:read` das Recht `guests:read`, E-Mail-Adresse und Telefonnummer nur mit der Handlung `action:guests.personal` (sonst `null`), jede Seite zählt gegen das Stundenkontingent für zeilenliefernde Gästeaufrufe (120 je Stunde und Schlüssel), und `recordsRead` wird je Antwort beziffert. Die Reihenfolge ist stabil (nach Kennung), aber ohne fachliche Bedeutung.
ZÄHLEN OHNE SPEICHERN: `POST /api/v1/marketing/segments/count` nimmt einen Bedingungsbaum und antwortet nur mit `{ total, email, sms }`. Das ist ein POST, weil ein Baum nicht in eine Adresse passt — trotzdem genügt `marketing:read`, denn es entsteht nichts und es geht keine Person hinaus. Jede Zählung läuft über die ganze Gästekartei; deshalb gilt eine eigene Bremse von 60 Zählungen je Minute und Betrieb, die sich `…/segments/count` und die frische Zählung in `GET …/lists/{id}` teilen.
VERWEISE MÜSSEN ZUM BETRIEB GEHÖREN. Eine Bedingung auf einen Bereich (`reservation.areaId`) oder eine Kampagne (`campaign.campaignId`) eines anderen Betriebs ist 400 mit `issues[].code = "invalid_reference"` am Feld `conditions`.
SCHREIBEN. Anlegen verlangt `Idempotency-Key`. `PATCH` ändert nur die geschickten Felder; neue `conditions` zählen die Liste neu. Solange eine Kampagne gerade ihre Empfänger aus dieser Liste bestimmt, sind neue Bedingungen 409 (`reason: "marketing-list-materializing"`) — sonst bekäme die erste Hälfte der Empfänger die alte, die zweite die neue Zielgruppe. Löschen ist 409 (`reason: "marketing-list-in-use"`, dazu `usage` mit den Namen), solange eine geplante, laufende oder pausierte Kampagne oder eine aktive bzw. pausierte Zeitplan-Automatisierung die Liste nutzt. Genau dann sind NEUE Bedingungen Versand: die Kampagne liest die Liste erst beim Start, die Automatisierung bei jedem Lauf — ein Schlüssel ohne `action:marketing.send` bekommt 403 `forbidden_action` (`reason: "marketing-list-live"`, dazu `usage`). Name, Beschreibung und unveränderte Bedingungen bleiben frei. Jeder Schreibaufruf läuft auch im Prüfmodus.