# K4-KONZEPT - Kurato Newsletter/Versand

Stand: 22.07.2026. Erstellt von Fable 5 als Architektur- und Etappenkonzept fuer die
Umsetzung durch Opus. Grundlage: BAUPLAN.md (Abschnitte 3, 13, 25, E-Mail-Versand),
DECISIONS.md (K1 bis K5), STATUS.md (Review-Backlog), bestehender Code in
`packages/hafenstudios-core/src/Mail/`, `plugins/kurato/src/Db/`, `plugins/leadlotse/src/Doi/`.

---

## Executive Summary

K4 macht Kurato zum Versandzentrum der Flotte: Listen mit Double-Opt-in, Segmente als
gespeicherte Filter, ein Gutenberg-basierter Mail-Editor mit Autosave, eine chunked
Versand-Queue mit konfigurierbarem Durchsatz und Brevo als EU-Default-Transport
(entschieden in K1, wird nicht neu aufgerollt). WordPress bleibt die Steuerzentrale
und einzige Datenquelle; der ESP ist reiner Zusteller. Tracking (Open/Click) ist per
Default AUS und braucht separaten Consent (CNIL-Linie). Der Review-Backlog-Punkt
"doppeltes lead_confirmed-Event" wird mit einem kleinen Leadlotse-Fix plus idempotentem
Kurato-Konsumenten erledigt. Sequenzen und Bounce-Automatik-Vollausbau bleiben wie
geplant in 1.1. K4 wird in fuenf Bau-Etappen K4a bis K4e geschnitten, jede mit
lauffaehigem Zwischenstand.

---

## 1. Versand-Architektur: WordPress vs. ESP

**Grundsatz: WordPress ist Steuerzentrale und Datenhoheit, der ESP ist der Briefträger.**

| Bleibt in WordPress (Kurato) | Macht der ESP (Brevo) |
|---|---|
| Kontakte, Listen, Abos, Consent-Log | Physische Zustellung einzelner Mails (API-Call je Mail) |
| Segment-Auswertung, Empfaengerauswahl | IP-Reputation, Retry auf SMTP-Ebene |
| Kampagnen-Erstellung, Editor, Personalisierung | Bounce-/Complaint-Erkennung, Rueckmeldung per Webhook |
| Queue, Drosselung, Suppression-Pruefung | |
| Abmeldung, Preference Center, DOI | |

Bewusst NICHT genutzt werden Brevos eigene Kontaktlisten und Kampagnen-Features.
Kein Kontakt-Sync zum ESP; Brevo sieht nur Einzelmails (transaktionaler API-Endpunkt).
Das haelt das Datenschutzversprechen ("Daten auf dem eigenen Server") und macht den
ESP austauschbar (SES/Postmark spaeter, gleiche Schnittstelle).

**Aufbau auf dem Core-Mailer** (`packages/hafenstudios-core/src/Mail/`):
- Das Interface `MailDriver` (send/key) und das Wertobjekt `Message` bleiben unveraendert.
  `Message.headers` traegt bereits beliebige Header (List-Unsubscribe etc.).
- Kurato liefert einen neuen Treiber `BrevoDriver implements MailDriver` (HTTP-API via
  `wp_remote_post`, API-Key libsodium-verschluesselt in den Settings, K-Muster Sicherheit).
- Zusaetzlich ein Kurato-Interface `EspDriver extends MailDriver` mit
  `send_detailed(Message): SendResult` (ok, provider_message_id, permanent_error).
  Der Core bleibt unberuehrt; die Queue nutzt `send_detailed`, wenn verfuegbar, sonst `send`.
- **Zwei Versandpfade**: (1) Transaktionsmails (DOI, Bestaetigungen, spaeter Termine)
  laufen weiter ueber `Core\Mailer` sofort und ohne Queue. (2) Marketing-Mails laufen
  ueber einen neuen Kurato-Service `CampaignMailer`, der seinen Treiber aus dem
  Kurato-Setting `mail.transport` (brevo | smtp | wp_mail, Installer.php ist vorbereitet)
  ueber eine `DriverFactory` bezieht. Optional kann der Betreiber per Schalter auch die
  Transaktionsmails ueber Brevo schicken (Kurato haengt sich dann in den bestehenden
  Filter `hafenstudios/mail_driver`).
- Marketing-Mails bekommen eigene Absenderdaten (Setting `sender`), Pflicht-Abmeldelink
  und die Header `List-Unsubscribe` + `List-Unsubscribe-Post: List-Unsubscribe=One-Click`
  (RFC 8058, harte Google/Yahoo-Anforderung seit 2025). Transaktionsmails ohne Abmeldelink.

## 2. Datenmodell K4 (Migration 1.1.0 in `Schema.php`)

Neue Migration `migrate_1_1_0`, DB_VERSION auf 1.1.0. Alle Tabellen via `Schema::table()`.

| Tabelle | Wichtigste Spalten | Zweck |
|---|---|---|
| `hs_lists` | id, slug (UNIQUE), label, description, doi_required (TINYINT, Default 1), created_at | Verteiler mit Einwilligungspflicht |
| `hs_subscriptions` | id, list_id, contact_id, status ENUM-artig VARCHAR (pending / confirmed / unsubscribed), doi_token_hash, consent_id, source VARCHAR, subscribed_at, confirmed_at, unsubscribed_at; UNIQUE(list_id, contact_id), KEY(status), KEY(contact_id) | Abo je Kontakt und Liste inkl. DOI-Status |
| `hs_segments` | id, label, definition LONGTEXT (JSON-Filter), created_at, updated_at | Gespeicherte Filter |
| `hs_campaigns` | id, post_id (CPT hs_mail), status (draft / scheduled / sending / paused / sent / aborted), audience LONGTEXT (JSON: Listen-IDs, Segment-IDs, Ausschluesse), scheduled_at, started_at, finished_at, total_recipients, sent_count, failed_count | Versandzustand einer Kampagne; Inhalt liegt im CPT |
| `hs_mail_queue` | id, campaign_id NULL, contact_id, to_email, status (pending / sending / sent / failed / skipped), attempts, claimed_at, scheduled_at, sent_at, provider_message_id, last_error; UNIQUE(campaign_id, contact_id), KEY(status, scheduled_at) | Versand-Warteschlange, Snapshot der Empfaenger |
| `hs_mail_events` | id, campaign_id NULL, contact_id, queue_id NULL, type (sent / bounce_hard / bounce_soft / complaint / open / click / unsubscribe), detail VARCHAR(255), created_at; KEY(campaign_id, type), KEY(contact_id) | Ereignis-Log; open/click nur bei Tracking-Consent |

Ergaenzungen an Bestandstabellen:
- `hs_contacts`: neue Spalten `email_status` VARCHAR (active / suppressed_bounce /
  suppressed_complaint), `tracking_consent` TINYINT Default 0, `last_activity_at`
  DATETIME NULL (denormalisiert, indexiert; gepflegt von Intake, Abo-Events und
  Mail-Events fuer die Segment-Auswertung).
- Mail-INHALT als CPT `hs_mail` (Block-Editor, Revisionen, Autosave gratis; BAUPLAN 25).

**In K4 v1** sind alle sechs Tabellen enthalten. **Verschoben auf 1.1** (BAUPLAN):
`hs_sequences`, `hs_sequence_steps`, `hs_sequence_enrollments` (Sequenzen), Soft-Bounce-
Zaehler und automatische Listenhygiene-Regeln (Bounce-Automatik). Die Events-Tabelle
sammelt ab v1 die Daten, auf denen 1.1 aufsetzt.

## 3. Listen, Double-Opt-in und die Leadlotse-Verzahnung

**Zustaendigkeit**: Leadlotse bleibt zustaendig fuer Formular-DOI (funktioniert standalone,
D9). Kurato uebernimmt ab K4 die Listen-/Abo-Verwaltung und hat einen EIGENEN DOI-Flow
fuer alle Nicht-Formular-Wege (Import, manuelles Hinzufuegen, Preference Center).

**Flow Formular-Anmeldung (Kurato aktiv):**
1. Newsletter-Formular abgesendet -> `hafenstudios/lead_created` -> `LeadIntake` legt
   Kontakt an (bestehend, unveraendert).
2. NEU: Ein `SubscriptionIntake` lauscht ebenfalls auf `lead_created`. Traegt der Payload
   `purpose = newsletter` (plus optional eine Ziel-Listen-Angabe aus den Formular-
   Einstellungen, sonst Default-Liste "Newsletter"), wird eine Subscription mit
   status = pending angelegt. Die DOI-Mail verschickt weiterhin Leadlotse (kein doppelter
   Mailversand).
3. Nutzer klickt den Leadlotse-Bestaetigungslink -> `hafenstudios/lead_confirmed` ->
   Kurato setzt die pending Subscription auf confirmed, speichert `confirmed_at` und
   verknuepft die Consent-Referenz.
4. Ohne Kurato bleibt alles wie heute (Entry-basiertes DOI).

**Backlog-Fix "doppeltes lead_confirmed-Event"** (STATUS.md): Ursache ist
`DoiService::confirm()` (Leadlotse): der Bereits-bestaetigt-Fall gibt idempotent den
Entry zurueck, und `PublicController::confirm()` feuert das Event bei JEDEM gueltigen
Aufruf erneut. Zweiteiliger Fix:
1. **Leadlotse (klein)**: `DoiService::confirm()` liefert zusaetzlich die Information,
   ob die Bestaetigung JETZT erfolgt ist (z.B. Rueckgabe `array(entry, newly_confirmed)`
   oder Flag im Entry-Array). `PublicController` emittiert `lead_confirmed` nur bei
   `newly_confirmed === true`. Der Nutzer sieht weiterhin in beiden Faellen die
   Erfolgsmeldung (idempotent bleibt idempotent).
2. **Kurato (Defense in depth)**: Der Subscription-Konsument ist trotzdem idempotent:
   Uebergang nur pending -> confirmed; confirmed -> confirmed ist ein No-op, UNIQUE
   (list_id, contact_id) verhindert Doppel-Abos. Damit ist Kurato auch gegen Replays
   und kuenftige weitere Event-Quellen robust.

**Kurato-eigenes DOI**: Token-Muster von `DoiService` uebernehmen (HMAC-signiert,
base64url, Hash in `hs_subscriptions.doi_token_hash`, Einmal-Verwendung), Public-Endpoint
`kurato/v1/confirm` mit Rate Limit. DOI-Mail geht als Transaktionsmail sofort raus.
Importierte Kontakte bekommen KEIN automatisches confirmed; der Betreiber muss beim
CSV-Import erklaeren, dass Einwilligungen vorliegen (Checkbox + Logeintrag), sonst pending.

**Abmeldung**: Public-Endpoint `kurato/v1/unsubscribe` mit signiertem Token je
(contact, list bzw. all). Ein Klick genuegt (One-Click, RFC 8058 POST wird ebenfalls
akzeptiert). Setzt status = unsubscribed, schreibt `hs_mail_events` (unsubscribe) und
den Consent-Widerruf ins Consent-Log.

## 4. Segmente

**Modell**: `hs_segments.definition` ist ein JSON mit flacher UND-Verknuepfung (v1):

```
{ "match": "all", "conditions": [
  { "field": "tag",           "op": "has|has_not",        "value": 12 },
  { "field": "stage",         "op": "in|not_in",          "value": [3,4] },
  { "field": "list",          "op": "subscribed",         "value": 5 },
  { "field": "last_activity", "op": "older_days|newer_days", "value": 30 }
] }
```

**Auswertung**: Ein `SegmentQuery`-Service kompiliert die Definition zu EINEM SQL
(JOINs auf `hs_tag_rel`, `hs_subscriptions`, Spalten `stage_id`, `last_activity_at`),
prepared, keine PHP-Schleife ueber alle Kontakte. Vorschau-Zaehler im Admin
("Segment trifft aktuell 143 Kontakte").

**Verwendung beim Versand**: Empfaengerauswahl einer Kampagne = Listen und/oder Segmente,
optional Ausschluss-Listen. Beim Start wird die Auswahl EINMAL materialisiert: Segment
auswerten, mit confirmed-Abos schneiden (Marketing-Mails gehen NUR an confirmed und
nicht supprimierte Adressen), Duplikate mergen, als Zeilen in `hs_mail_queue` snapshotten.
Spaetere Kontakt-Aenderungen beeinflussen einen laufenden Versand nicht (nur die
Suppression wird beim tatsaechlichen Send noch einmal geprueft).

OR-Verknuepfung, verschachtelte Gruppen und weitere Felder (Quelle/UTM, Formular X
abgesendet) sind 1.1+; das JSON-Format traegt das ohne Migration (analog D14).

## 5. DNS-Check-Flow (SPF/DKIM/DMARC) im Onboarding

**Wann**: Schritt im Kurato-Versand-Onboarding (Core-Wizard-Framework aus D15 nutzen),
erneut pruefbar per Button; Ergebnis 12 h gecacht (Transient), Aenderung des Absenders
invalidiert den Cache.

**Wie pruefen** (`DnsCheckService`, PHP `dns_get_record`, kein externer Call):
- SPF: TXT der Absender-Domain enthaelt `v=spf1`; bei Transport Brevo zusaetzlich
  Heuristik, ob das Brevo-Include vorhanden ist.
- DKIM: TXT/CNAME am erwarteten Selektor (je Transport hinterlegte Default-Selektoren,
  bei SMTP freies Selektor-Feld).
- DMARC: TXT an `_dmarc.<domain>` mit `v=DMARC1` (p=none reicht fuer gruen, Empfehlung
  quarantine wird nur als Hinweis angezeigt).
- Fallback: Wenn `dns_get_record` vom Hoster blockiert ist, KEIN stiller externer Call
  (Markenversprechen). Stattdessen Status "nicht pruefbar" mit manueller Anleitung;
  DNS-over-HTTPS als expliziter Opt-in-Button ("Ueber Cloudflare-DNS pruefen?") ist
  eine offene Entscheidung (Punkt 11).

**Wie erklaeren (Hans)**: Ampel je Eintrag plus ein Satz in Alltagssprache
("SPF sagt den Empfaengern, dass Brevo in Ihrem Namen senden darf. Ohne diesen Eintrag
landen Ihre Mails im Spam oder werden abgewiesen."). Je Transport eine Copy-Paste-Box
mit den exakt zu setzenden DNS-Records und dem Hinweis, das dem Hoster/IT-Betreuer zu
geben. Kein Fachbegriff ohne Erklaerungssatz.

**Wann blockieren**: Kampagnen-Versand (Marketing) ist blockiert, solange SPF ODER DKIM
rot sind; fehlendes DMARC ist gelb (Warnung, kein Block, aber Hinweis auf Google/Yahoo-
Pflicht ab 5.000 Mails/Tag). Testversand an die eigene Adresse und Transaktionsmails
bleiben immer moeglich (sonst ist DOI tot). Der Block ist ein Server-seitiger Check im
Kampagnen-Start (nicht nur UI).

## 6. Versand-Queue

**Scheduler-Entscheidung: Action Scheduler**, als Bibliothek in Kurato gebuendelt
(BAUPLAN 25 verlangt das explizit; gleiches Embed-Muster wie der Core selbst, D2).
Begruendung gegen reines WP-Cron: nebenlaeufigkeitssicheres Claiming, Admin-Sichtbarkeit,
bewaehrt bei WooCommerce-Scale. WP-Cron bleibt darunter der Ausloeser (mit Hinweis im
Onboarding auf echten System-Cron bei groesseren Listen).

**Ablauf**:
1. Kampagnen-Start: Empfaenger materialisieren (Abschnitt 4) -> `hs_mail_queue`
   pending, Kampagne -> sending. Materialisierung selbst chunked (z.B. 500er-Schritte
   als Action-Scheduler-Jobs), damit auch 50k-Listen auf Shared Hosting starten.
2. Wiederkehrende Action `kurato_process_queue` (jede Minute): claimt einen Batch
   (status pending, `scheduled_at <= now`, `claimed_at` setzen, Batchgroesse =
   Stundenrate / 60, Minimum 1), rendert die Mail je Empfaenger (Personalisierung),
   prueft Suppression und Abo-Status final, sendet ueber den Treiber, schreibt
   sent/failed + `hs_mail_events` (sent).
3. Crash-Recovery: sending-Zeilen mit `claimed_at` aelter als 15 min fallen auf pending
   zurueck; max. 3 Versuche mit Backoff, danach failed mit `last_error`.
4. Kampagne -> sent, wenn keine pending/sending-Zeilen mehr; Zaehler auf `hs_campaigns`.

**Durchsatz konfigurierbar** (Setting `mail.rate_per_hour`), Presets mit Hans-Text:
"Shared Hosting SMTP: 100 bis 200 pro Stunde" (Research-Befund), "Brevo: nach Ihrem
Brevo-Tarif". Pause/Abbruch je Kampagne (status paused/aborted, Queue-Rest wird skipped).

**Trennung Transaktion vs. Marketing**: Transaktionsmails (DOI, Bestaetigungen) gehen
NIE durch die Marketing-Queue und zaehlen nicht gegen die Rate; eigener Absender
moeglich; kein Abmeldelink. Nur Marketing-Mails tragen List-Unsubscribe-Header.

## 7. Tracking-Consent (CNIL-Linie)

**Rechtlicher Rahmen**: Open-/Click-Tracking ist einwilligungspflichtig, getrennt von
der Newsletter-Einwilligung. **Default: AUS**, auf beiden Ebenen:
- Site-Ebene: Tracking-Feature global aus, Betreiber muss es aktiv einschalten
  (mit Datenschutz-Hinweistext).
- Kontakt-Ebene: `hs_contacts.tracking_consent` (Default 0). Erhoben ueber eine separate,
  nicht vorangekreuzte Checkbox (Leadlotse-Newsletter-Vorlage optional erweitert;
  Preference Center als Toggle), Nachweis im Consent-Log (eigener Consent-Typ
  `mail_tracking`, Textversion + Zeitstempel, Core `ConsentLog::record()`, D6).

**Technische Umsetzung**: Beim Rendern je Empfaenger in der Queue entscheidet sich:
- Open-Pixel (`kurato/v1/o/{token}`) wird NUR eingefuegt, wenn Site-Schalter an UND
  Kontakt-Consent vorliegt.
- Click-Tracking: Links werden NUR dann auf den Redirect (`kurato/v1/r/{token}`)
  umgeschrieben; ohne Consent bleiben Original-Links stehen.
- Tokens HMAC-signiert (campaign_id, contact_id, URL-Hash), Rate Limit, keine
  IP-Speicherung (Event ohne IP, BAUPLAN 28). 302-Redirect validiert die Ziel-URL
  gegen die beim Rendern hinterlegte Liste (kein Open-Redirect).
- Kampagnen-Statistik ohne Consent bleibt aussagekraeftig: versendet, Bounces,
  Abmeldungen sind Server-Events und brauchen kein Tracking. Open-/Klickraten werden
  mit Bezugsgroesse "n Empfaenger mit Tracking-Einwilligung" ausgewiesen, damit die
  Zahlen ehrlich sind.

## 8. Mail-Editor

- **Basis**: CPT `hs_mail` im Block-Editor mit eingeschraenktem Block-Set (Absatz,
  Ueberschrift, Bild, Button, Liste, Trenner, Spacer). **Autosave und Revisionen kommen
  damit gratis von WordPress**; genau der FluentCRM-Schwachpunkt ist damit ohne
  Eigenbau geloest. Kein eigener Editor, kein Build-Schritt (Linie D13/D14).
- **E-Mail-HTML-Rendering**: Eigener `EmailRenderer` uebersetzt die Block-Ausgabe in
  mailclient-taugliches HTML: ein einspaltiges Basis-Template (600 px, Tabellen-Layout,
  Inline-CSS, Dark-Mode-tauglich), Blocks werden auf eine kleine Menge geprüfter
  Markup-Muster abgebildet. Mehrspaltigkeit bewusst NICHT in v1 (groesste Fehlerquelle
  in Outlook). Plain-Text-Alternative wird automatisch erzeugt (Multipart).
- **Personalisierung**: Platzhalter `{{first_name|Fallback}}`, `{{display_name}}`,
  `{{email}}`, `{{unsubscribe_url}}`, `{{preferences_url}}`; Ersetzung je Empfaenger
  in der Queue; Vorschau mit Beispielkontakt; Testversand an eigene Adresse.
- **Pflicht-Abmeldelink technisch erzwungen**: Der Renderer haengt einen Standard-Footer
  (Absender-Anschrift + Abmeldelink + Praeferenzen-Link) an, wenn `{{unsubscribe_url}}`
  im Inhalt fehlt. Nicht abschaltbar. Der Kampagnen-Start validiert zusaetzlich.
- **Preference Center light**: Oeffentliche, per signiertem Link erreichbare Seite
  (kein Login): zeigt alle Listen des Kontakts mit Abmelden je Liste, "Alle abmelden"
  und den Tracking-Consent-Toggle. Vollausbau (Profildaten aendern) spaeter (Phase 4).

## 9. Bounce-/Complaint-Handling

**v1 (K4e), Minimal-Hygiene, weil zustellbarkeitskritisch**:
- Inbound-Webhook `kurato/v1/webhooks/brevo` (Secret-Token in URL, Payload-Pruefung,
  Rate Limit). Mapping: hard_bounce/blocked -> `bounce_hard`, soft_bounce ->
  `bounce_soft`, spam -> `complaint`, unsubscribe -> `unsubscribe`.
- Wirkung: Event in `hs_mail_events`; bei `bounce_hard` und `complaint` sofort
  `hs_contacts.email_status = suppressed_*`. Die Queue ueberspringt supprimierte
  Adressen bei jedem Send (skipped). Complaint setzt zusaetzlich alle Abos auf
  unsubscribed (0,3-Prozent-Regel von Google/Yahoo schuetzt man nur so).
- Kontaktprofil zeigt den Zustellstatus ("Adresse unzustellbar seit ...").
- SMTP-Transport hat KEIN Bounce-Handling in v1 (kein Postfach-Polling); Hans sagt das
  ehrlich im Onboarding ("Mit Brevo erkennt Kurato unzustellbare Adressen automatisch").

**1.1 (verschoben, BAUPLAN)**: Soft-Bounce-Zaehler mit Schwellwert-Suppression,
automatische Listenhygiene-Regeln (Pro "Bounce-Sync"), Reaktivierungs-Workflows,
Reporting-Vollausbau, weitere ESP-Webhooks (SES/Postmark) hinter einem gemeinsamen
`WebhookNormalizer`-Interface, das in v1 schon so geschnitten wird.

## 10. Etappenplan K4

1. **K4a - Listen, Abos, DOI-Verzahnung** (Fundament, kein Versand noetig):
   Migration 1.1.0 (alle 6 Tabellen + Kontakt-Spalten), ListRepository +
   SubscriptionRepository, Kurato-DoiService + `kurato/v1/confirm` + `unsubscribe`,
   SubscriptionIntake (lead_created -> pending, lead_confirmed -> confirmed),
   **Leadlotse-Fix doppeltes lead_confirmed** (DoiService-Rueckgabe + Controller),
   Listen-Admin (CRUD), Abo-Anzeige im Kontaktprofil, CSV-Import mit
   Einwilligungs-Erklaerung. Tests: Intake-Idempotenz, Token-Roundtrip, Statusuebergaenge.
2. **K4b - Transport und Queue**: BrevoDriver + EspDriver/SendResult, DriverFactory +
   Setting `mail.transport`, verschluesselte Credentials, DnsCheckService + Onboarding-
   Wizard (Ampel, Hans-Texte, Server-seitiger Versand-Block), Action Scheduler einbetten,
   `hs_mail_queue`-Verarbeitung (Claim, Rate, Retry, Recovery), Testversand-Endpoint.
   Tests: Queue-Statusmaschine, Rate-Berechnung, DNS-Parser gegen Fixtures.
3. **K4c - Editor und Kampagnen**: CPT `hs_mail` + Block-Whitelist, EmailRenderer
   (Template, Inline-CSS, Plain-Text, Pflicht-Footer), Personalisierung, Kampagnen-UI
   (Empfaenger = Listen, Betreff/Absender, Vorschau, Testversand, Planen/Starten),
   Materialisierung -> Queue, Kampagnenliste mit Fortschritt, Pause/Abbruch.
4. **K4d - Segmente und Preference Center**: hs_segments-CRUD, SegmentQuery (SQL),
   Segment-Builder-UI (Bedingungszeilen, Live-Zaehler), Segmente als Kampagnen-Empfaenger,
   Preference Center light (inkl. Tracking-Consent-Toggle).
5. **K4e - Webhooks, Suppression, Statistik, Tracking**: Brevo-Webhook + Suppression-
   Logik, Kampagnen-Statistik (versendet/Bounces/Abmeldungen; Open/Klick nur bei Consent),
   Open-Pixel + Click-Redirect hinter doppeltem Consent-Gate, Consent-Typ `mail_tracking`,
   PrivacyBridge-Erweiterung (Abos, Queue- und Event-Zeilen in Export/Loeschung),
   1 Willkommensmail-Automatik (Free-Versprechen: eine Mail bei Abo-Bestaetigung,
   bewusst OHNE Sequenz-Unterbau, einfacher Hook auf subscription_confirmed).

**Bewusst verschoben**: Sequenzen (1.1), Bounce-Automatik-Vollausbau und Soft-Bounce-
Regeln (1.1), weitere ESP-Treiber SES/Postmark (Abstraktion steht), Mehrspalten-Layouts
im Editor, OR/Nested-Segmente, Reporting-Vollausbau, Team-Rollen. Neue Flotten-Events in
K4: `subscription_pending`, `subscription_confirmed`, `subscription_unsubscribed`,
`campaign_sent` (Sequenzen und Kompass docken 1.1/Phase 3 daran an).

## 11. Offene Entscheidungen fuer den Inhaber

1. **Brevo-Konto/Partnerschaft**: Affiliate-/Tech-Partnerschaft anfragen (BAUPLAN
   offene Frage 6)? Beeinflusst Onboarding-Texte und ggf. Sonderkonditionen.
2. **DNS-Fallback per DNS-over-HTTPS**: Als expliziter Opt-in-Button erlaubt, oder
   strikt "kein externer Call" und nur manuelle Anleitung?
3. **Free/Pro-Schnitt Segmente**: BAUPLAN nennt "Segmente-Vollausbau" als Pro.
   Vorschlag: v1-Segmente (UND-Filter) Free, OR/verschachtelt + Aktivitaets-Felder Pro.
4. **Tracking-Formulierung**: Wortlaut der Tracking-Consent-Checkbox und des
   Datenschutz-Bausteins final abnehmen (rechtlicher Feinschliff).
5. **Import-Policy**: CSV-Import mit Einwilligungs-Selbsterklaerung ausreichend, oder
   zusaetzlich erzwungenes Re-Permission-Mailing fuer Importe ohne Nachweis?
6. **Brevo auch fuer Transaktionsmails als Default-Empfehlung**, sobald konfiguriert
   (technisch vorbereitet, eine Empfehlungs-/Default-Frage)?

---

## Getroffene Entscheidungen (Inhaber, 22.07.2026)

- **Import-Policy**: Selbsterklaerung per Checkbox + Consent-Log-Eintrag beim CSV-Import;
  ohne Bestaetigung landen Kontakte auf `pending`. Kein erzwungenes Re-Permission-Mailing.
- **DNS-Fallback**: BEIDE Wege einbauen. Default ist strikt kein externer Call
  (`dns_get_record` lokal). Ist das blockiert oder wuenscht der Nutzer es, kann er im
  Install-/Versand-Assistenten DNS-over-HTTPS (z.B. Cloudflare) explizit freischalten
  (Opt-in, transparent, abschaltbar). Ohne Freischaltung: Status "nicht pruefbar" +
  manuelle Anleitung.
- **Segmente Free/Pro**: UND-Filter (Tag, Stufe, Liste) sind Free; OR-Verknuepfung,
  verschachtelte Gruppen und Aktivitaets-Filter (seit X Tagen inaktiv) sind Pro.
- Offene Detailfragen mit Default: Brevo-Partnerschaft anfragen (ja); Brevo nach
  Einrichtung auch fuer Transaktionsmails empfehlen (ja); finaler Tracking-Checkbox-
  Wortlaut = rechtlicher Feinschliff spaeter.

## Technischer Realitaets-Check (Opus)

- **Action Scheduler ist NICHT im Projekt vorhanden.** Der Core nutzt nur das
  Versionierungs-*Muster* (D2), nicht das Paket. In K4b wird `woocommerce/action-scheduler`
  als Composer-Abhaengigkeit neu eingebettet (Init ueber die uebliche
  `ActionScheduler_...`-Bootstrap-Datei, geladen wenn noch keine hoehere Version aktiv ist).

---

*Referenzen: BAUPLAN.md Abschn. 3, 13, 25 und "E-Mail-Versand"; DECISIONS.md K1-K5, D6,
D9, D13-D15; STATUS.md Review-Backlog; Code: Core Mail (Mailer, MailDriver, Message),
Kurato Schema/Installer, Leadlotse DoiService/PublicController.*
