📬 Mail-API

mail-api.schaefer.zone  Â·  Strict Mail API for the AI agent  Â·  Status: online

Purpose: Dieser Endpunkt erlaubt einer KI das Lesen und Verschieben von E-Mails aus dem Postfach michael@schaefer.zone. Es gelten strenge Regeln:

Authentication

Alle Endpunkte außer /, /health und /admin/* erfordern einen Bearer-Token im Authorization-Header:

Authorization: Bearer <API_TOKEN>

API Endpoints

GET  /health

No auth. Liveness check.

{ "status": "ok", "ts": 1718880000 }

GET  /next

Holt die nächste berechtigte Mail (älter als 10h, nicht geblockt), lockt sie und liefert die vollständige Mail (Headers + Body) zurück.

Wenn bereits eine Mail gelockt ist: liefert 200 OK mit derselben bereits gelockten Mail zurück (zusätzlich Feld "lock_reissued": true). /next rückt also niemals vor, solange die alte Mail nicht per /move oder /skip abgearbeitet wurde. Ein abgestürzter Client kann somit einfach /next erneut aufrufen.

Der 5-Minuten-Auto-Release greift nur, falls der Server selbst abstirbt oder der Client gar nicht mehr antwortet.

// 200 OK (neue Mail gelockt)
{
  "uid": 87951,
  "uidvalidity": 1730000000,
  "folder": "INBOX",
  "from": { "name": "GitLab", "addr": "gitlab@mg.gitlab.com" },
  "to": [{ "name": null, "addr": "michael@schaefer.zone" }],
  "cc": [],
  "subject": "SessionVault | Successful pipeline...",
  "date": "2026-06-20T09:37:00.000Z",
  "message_id": "<...>",
  "in_reply_to": null,
  "headers": { ... },
  "body": { "text": "Plain text body", "html": "<html>...</html>" },
  "attachments": [
    { "filename": "invoice.pdf", "mime": "application/pdf", "size": 124356, "content_id": null }
  ],
  "has_attachment": true,
  "flags": [],
  "locked_at": 1718880000,
  "expires_at": 1718880300,
  "lock_reissued": false
}

// 200 OK (Lock bereits aktiv – gleiche Mail erneut)
{ ...gleiche Struktur..., "lock_reissued": true }

// 404 Not Found
{ "error": "no_eligible_mail", "message": "Keine berechtigte Mail gefunden." }

GET  /current

Liefert die aktuell gelockte Mail erneut (oder 404 falls keine aktiv).

GET  /current/attachments/:index

Liefert den Anhang mit Index :index (0-basiert) der aktuell gelockten Mail als Binary-Stream mit passenden Content-Type- und Content-Disposition-Headern.

POST  /move

Verschiebt die aktuell gelockte Mail in den angegebenen AI-Subordner und hebt den Lock auf.

// Request body
{ "to": "AI.Aufbewahren.Kaeufe" }

// 200 OK
{ "status": "moved", "uid": 87951, "from": "INBOX", "to": "AI.Aufbewahren.Kaeufe" }

// 400 (Target nicht erlaubt)
{ "error": "invalid_target", "message": "Target muss mit 'AI.' beginnen." }

// 409 (kein aktiver Lock)
{ "error": "no_active_lock" }

POST  /skip

Überspringt die aktuell gelockte Mail. Pflichtfelder: reason und category. Die Mail wird dauerhaft geblockt, bis ein Admin sie freigibt.

// Request body
{
  "reason": "Mail enthält Passwort-Reset-Link – kann nicht beurteilen ob Phishing.",
  "category": "uncertain_security",   // uncertain_security | private_sensitive | needs_human | spam_suspect | other
  "confidence": 0.3                    // optional 0.0–1.0
}

// 200 OK
{ "status": "skipped", "uid": 87951, "blocked_until": "admin_release" }

// 400 (reason/category fehlen)
{ "error": "missing_fields", "fields": ["reason", "category"] }

GET  /folders

Listet alle AI-Subordner auf (nur AI.*, nichts anderes).

{
  "folders": [
    { "path": "AI.Spam", "name": "Spam", "special": null },
    { "path": "AI.WICHTIG.Persoenlich", "name": "Persoenlich", "special": null }
  ],
  "count": 15
}

POST  /folders

Legt einen neuen AI-Subordner an. Blockt bei Überschreitung von 30 direkten Subordnern.

// Request body
{ "name": "AI.Aufbewahren.XYZ" }

// 200 OK
{ "status": "created", "path": "AI.Aufbewahren.XYZ" }

// 400 (Max überschritten)
{ "error": "max_subfolders_reached", "current": 30, "max": 30 }

POST  /restructure-request

Stellt einen Antrag auf Ordner-Umstrukturierung (rename/merge/delete). Wird im Admin-Bereich entschieden.

// Request body
{
  "action": "rename",   // rename | merge | delete
  "from": "AI.Aufbewahren.Kaeufe",
  "to": "AI.Aufbewahren.Bestellungen",
  "reason": "Besserer Standardbegriff"
}

// 200 OK
{ "status": "request_submitted", "id": 14 }

GET  /audit

Liefert die letzten Audit-Einträge (read-only).

Query-Parameter: ?limit=200, ?since=1718870000 (Unix-Sekunden).


Admin UI

Der Admin-Bereich ist unter /admin erreichbar und benötigt ein separates Passwort (Session-Cookie). Er bietet:


Beispiel-Workflow eines AI-Agents

# 1. Nächste Mail holen
curl -H "Authorization: Bearer $TOKEN" \
     https://mail-api.schaefer.zone/next

# 2. Falls lock_reissued=true (Lock war noch aktiv): dieselbe Mail erneut
#    Einfach weiterarbeiten – kein Warten nötig.
curl -H "Authorization: Bearer $TOKEN" \
     https://mail-api.schaefer.zone/current

# 3. Falls Anhang vorhanden, gezielt laden
curl -H "Authorization: Bearer $TOKEN" \
     https://mail-api.schaefer.zone/current/attachments/0 -o file.pdf

# 4. Entscheiden: verschieben oder überspringen
curl -X POST -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"to":"AI.Aufbewahren.Kaeufe"}' \
     https://mail-api.schaefer.zone/move

# ODER
curl -X POST -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"reason":"Unklar","category":"needs_human","confidence":0.2}' \
     https://mail-api.schaefer.zone/skip

# 5. Nächste Mail (erst jetzt möglich)
curl -H "Authorization: Bearer $TOKEN" https://mail-api.schaefer.zone/next

© 2026 Michael Schäfer · Hosted on vps1.schaefer.zone · Source: /root/2026-06_ai-mail/api/