diff --git a/SPEC.md b/SPEC.md
index f6efb42..3bd8b47 100644
--- a/SPEC.md
+++ b/SPEC.md
@@ -194,6 +194,12 @@ Unverändert gegenüber v1:
- **Mehrere Empfänger je Kontakt, Vorlauf-Erinnerungen**
(z. B. "in 3 Tagen") sind funktional einfach nachrüstbar, aktuell
aber nicht Teil des Scopes.
+- **Contact Detail Page: UID statt DB-ID**: Der aktuelle Lookup
+ `WHERE id = %s` nutzt die Auto-Increment-ID, die sich bei Re-Syncs
+ ändern kann. Stabilere Alternative: `WHERE uid = %s AND account = %s`,
+ da die UID aus CardDAV konstant bleibt. Erfordert Änderungen an
+ Routes (`/contacts/{account}/{uid}`), Templates, Mailer-URLs und
+ Schema. Details siehe `feature-contact-uid-lookup.md`.
## 12. Web-Ansicht und API (v3, im selben Repo/Image)
diff --git a/feature-contact-uid-lookup.md b/feature-contact-uid-lookup.md
new file mode 100644
index 0000000..26d0be6
--- /dev/null
+++ b/feature-contact-uid-lookup.md
@@ -0,0 +1,129 @@
+# Feature: Contact Detail Page — UID statt DB-ID
+
+## Status
+
+**Geplant (Backlog)** — Kein aktueller Bedarf, aber sinnvolles Zukunftsfuture.
+
+## Ausgangslage
+
+Die Contact-Detail-Seite wird aktuell über die interne Datenbank-ID (`id INT AUTO_INCREMENT`) aufgerufen:
+
+```
+/contacts/{contact_id} (HTML)
+/api/contacts/{contact_id} (JSON)
+```
+
+Die `id` kann sich bei einem Re-Sync (z.B. nach Datenverlust) ändern. Die **UID** aus CardDAV ist hingegen stabil und bleibt über Synchronisationen hinweg gleich.
+
+## Problem
+
+- Die `id` ist kein stabiler Identifier — sie kann sich ändern
+- Bei einem Re-Sync mit neuem Schema werden alle alten IDs ungültig
+- Geteilte Links oder Lesezeichen auf `/contacts/42` brechen
+
+## Lösungsidee
+
+Den Lookup von `WHERE id = %s` auf `WHERE uid = %s` umstellen.
+
+### Einschränkung
+
+UIDs sind pro Account eindeutig (`UNIQUE KEY (account, uid)`), nicht global. Für Admin-Nutzer mit `show_all=True` müsste der Account ebenfalls im Lookup berücksichtigt werden.
+
+### Mögliche URL-Patterns
+
+| Ansatz | URL | Vorteil | Nachteil |
+|--------|-----|---------|----------|
+| UID only | `/contacts/{uid}` | Einfach | Nur sicher, wenn UID global eindeutig |
+| Account + UID | `/contacts/{account}/{uid}` | Explizit, immer korrekt | Längere URL, Admin-Modus nötig |
+| Komposit | `/contacts/{account}--{uid}` | Ein Path-Parameter | Unschön |
+
+**Empfehlung:** Account + UID (`/contacts/{account}/{uid}`) — sauber und eindeutig.
+
+## Betroffene Dateien
+
+### 1. `src/api/main.py` — API-Routen
+
+**JSON API (Zeile 127-149):**
+- Route: `GET /api/contacts/{contact_id}` → `GET /api/contacts/{account}/{contact_uid}`
+- Parameter: `contact_id: int` → `account: str, contact_uid: str`
+- Query: `WHERE id = %s` → `WHERE uid = %s AND account = %s`
+
+**HTML Detail (Zeile 578-601):**
+- Route: `GET /contacts/{contact_id}` → `GET /contacts/{account}/{contact_uid}`
+- Parameter: `contact_id: int` → `account: str, contact_uid: str`
+- Query: `WHERE id = %s` → `WHERE uid = %s AND account = %s`
+
+### 2. `src/api/templates/index.html` — Kontaktliste
+
+**Zeile 193:**
+```html
+
+
+
+
+
+```
+
+### 3. `src/api/templates/dashboard.html` — Geburtstagsliste
+
+**Zeile 309:**
+```html
+
+
+
+
+
+```
+
+### 4. `src/mailer.py` — Birthday-Mails
+
+**Zeile 25:** SELECT um `uid` erweitern (oder `id` entfernen):
+```sql
+SELECT account, uid, full_name, ...
+```
+
+**Zeile 84 (Plain Text):**
+```python
+# Aktuell
+line += f" → {Config.WEB_URL.rstrip('/')}/contacts/{b['id']}"
+# Neu
+line += f" → {Config.WEB_URL.rstrip('/')}/contacts/{b['account']}/{b['uid']}"
+```
+
+**Zeile 95 (HTML):**
+```python
+# Aktuell
+contact_link = f"{Config.WEB_URL.rstrip('/')}/contacts/{b['id']}"
+# Neu
+contact_link = f"{Config.WEB_URL.rstrip('/')}/contacts/{b['account']}/{b['uid']}"
+```
+
+### 5. `src/api/schemas.py` — Pydantic Models
+
+- `ContactOut.id` (Zeile 9): Kann bleiben (nützlich für interne Zwecke), aber `uid` wird primärer Lookup-Key
+- `GroupMemberOut.id` (Zeile 53): Prüfen ob noch benötigt
+
+### 6. `src/db.py` — DB-Queries
+
+SELECT-Klauseln enthalten bereits `uid`. `id` kann aus SELECTs entfernt werden, wo es nicht gebraucht wird.
+
+### 7. Dokumentation
+
+- `README.md` (Zeilen 166, 244, 247): Endpoint-Pattern aktualisieren
+- `SPEC.md` (Zeilen 253, 256): Endpoint-Pattern aktualisieren
+
+## Risiken
+
+1. **Backward-Kompatibilität:** Bestehende URLs (`/contacts/42`) brechen. Kein eleganter Redirect möglich (alte ID sagt nichts über UID aus).
+2. **UID-Encoding:** UIDs aus CardDAV können Sonderzeichen enthalten — `urlencode` in Templates ist Pflicht.
+3. **Admin-Modus:** Bei `show_all=True` ist `account_name` aktuell `None`. Der neue Ansatz mit explizitem Account-Pfad löst das sauber auf.
+
+## Umsetzungsreihenfolge
+
+1. `src/api/main.py` — Routen ändern (API + HTML)
+2. `src/api/templates/index.html` — Links anpassen
+3. `src/api/templates/dashboard.html` — Links anpassen
+4. `src/mailer.py` — URL-Bau anpassen
+5. `src/api/schemas.py` — `GroupMemberOut.id` prüfen
+6. `README.md` + `SPEC.md` — Dokumentation aktualisieren
+7. `ruff check src/` — Lint-Check