Warum du das willst
mailcow bringt eine Admin-API mit. Die ist super, um Domains, Postfächer und Aliase anzulegen, sagt aber nichts über die Mails in einem Postfach aus. Wenn ein Skript, ein CRM oder ein Automatisierungs-Workflow ein gemeinsames Postfach lesen, eine Antwort als diese Adresse senden oder eine Nachricht in einen Ordner einsortieren soll, lässt dich die Admin-API allein.
Die Mailbox API für mailcow schließt diese Lücke. Sie gibt deinen eigenen Programmen eine kontrollierte REST-Schnittstelle auf die Mails selbst: Ordner und Nachrichten auflisten, suchen, eine vollständige Nachricht mit Anhängen lesen, als das Postfach senden sowie als gelesen markieren, verschieben oder löschen. Darüber hinaus kann jedes überwachte Postfach jede neue Nachricht als signiertes JSON an einen https-Webhook und an einen Telegram-Chat schicken. Alles läuft in deinem mailcow-Netz, und du entscheidest, welche Postfächer und welche Rechte jeder Schlüssel bekommt.
Diese Anleitung führt durch das Setup, so wie ich es auf meinem eigenen Host gemacht habe: zuerst die Schlüssel, dann Lesen und Senden, dann Webhooks in ein Automatisierungstool wie n8n und zum Schluss Telegram-Benachrichtigungen.
Voraussetzungen
- mailcow-dockerized auf Linux mit docker compose und systemd sowie das installierte mailcow Toolkit 0.5.0 oder neuer von MediaLab Works (dieses Produkt ist eines seiner Module mit eigener Lizenz).
- Root-Zugriff auf dem Host, um den Toolkit-Installer auszuführen, und ausgehendes HTTPS vom Server zu
medialab.worksfür die Lizenzprüfung. - Eine https-Adresse für die API, die in signierten Anhangslinks verwendet wird, und für Webhooks einen eigenen https-Endpunkt.
- Optional: ein Telegram-Bot-Token von @BotFather und die Chat-ID jedes Postfachs, das du benachrichtigen willst.
- Das Modul ist Open Source (GPL-3.0), du bekommst also den vollständigen Quellcode.
Schritt 1 — Modul installieren und Lizenz aktivieren
Das Modul steckt im Toolkit-Paket. Auf dem mailcow-Host führst du als root den Installer wie gewohnt aus:
sudo ./install.sh --mailcow-dir /opt/mailcow-dockerized --name <instance>
Er kopiert data/web/mlw-mailbox-api/, fügt den Menüeintrag, den Host-Agent-Job und den Timer im Minutentakt hinzu und lädt php-fpm neu. Danach öffnest du im mailcow-Admin-Menü System → Mailbox API; dort siehst du ein Lizenz-Banner und darunter die Einstellungen.
Zum Aktivieren öffnest du System → Updates (Toolkit) und suchst die Karte Mailbox API für mailcow: Schlüssel einfügen und aktivieren oder die Testphase starten. Auf der Shell geht dasselbe mit php /web/mlw-toolkit/cli.php activate mailcow-mailbox-api <KEY> innerhalb von php-fpm-mailcow. Die Lizenz wird täglich geprüft; ohne Internet läuft das Modul nach der letzten erfolgreichen Prüfung noch 7 Tage weiter. Ohne gültige Lizenz antwortet die API mit 503 not_licensed und der Timer sendet nichts, Postfächer und Nachrichten bleiben aber unangetastet.
Schritt 2 — API einschalten und Grundlagen setzen
In System → Mailbox API:
- API on schaltet die API ein oder aus. Die Lizenz wird trotzdem geprüft.
- Public API address ist die Adresse in signierten Anhangslinks, zum Beispiel
https://mail.example.com/mlw-mailbox-api/. Leer lassen, um sie aus der Seite abzuleiten. - Messages per page (1–200) und largest attachment (1–100 MB) setzen deine Grenzen.
- Dovecot master user ist optional und nur nötig, wenn du in
mailcow.confeinen eigenen setzt; standardmäßig liest das Modul die Master-Zugangsdaten, die mailcow beim Start anlegt. - Telegram bot token ist vorerst optional; darauf kommen wir in Schritt 6 zurück.
Schritt 3 — Schlüssel mit begrenzten Rechten anlegen
In der Karte API keys gibst du dem Schlüssel einen Namen, hakst die benötigten Rechte an — read, send, organize —, wählst die Postfächer, die er nutzen darf, und drückst Create key. Der Schlüssel (mbx_…) wird nur einmal beim Anlegen angezeigt, speichere ihn also sofort. Die Liste zeigt Präfix, Rechte, Postfächer und letzte Nutzung; Revoke deaktiviert ihn sofort. In der Datenbank liegt nur der SHA-256 eines Schlüssels, und jeder Aufruf wird protokolliert.
Das ist der Teil, der mir für Automatisierung am besten gefällt: Ein Workflow, der eingehende Mail nur in Ordner einsortiert, bekommt einen Schlüssel mit organize auf einem Postfach und sonst nichts. Ein Benachrichtigungsskript bekommt nur read. Du gibst nie einen Schlüssel heraus, der alle Postfächer auf dem Server erreichen kann.
Schicke den Schlüssel bei jedem Aufruf mit:
Authorization: Bearer mbx_…
Schritt 4 — Mails lesen und organisieren
Alle Routen rufst du auf https://<mail host>/mlw-mailbox-api/?r=<route> auf — nginx in mailcow kennt kein PATH_INFO, die Route steckt also im Query-String. Antworten sind JSON. Jede Route außer dem signierten Anhangs-Link braucht den Schlüssel, und der Schlüssel muss das Postfach und das Recht erlauben.
Prüfe zuerst, was ein Schlüssel kann:
curl -H "Authorization: Bearer $KEY" \
"https://mail.example.com/mlw-mailbox-api/?r=/v1/me"
GET /v1/me liefert Schlüsselname, Rechte, Postfächer und Grenzen; GET /v1/mailboxes listet die erlaubten Postfächer mit Ordneranzahl und INBOX-UIDNEXT. Um die Ordner und eine Seite der neuesten Nachrichten zu sehen:
curl -H "Authorization: Bearer $KEY" \
"https://mail.example.com/mlw-mailbox-api/?r=/v1/folders&mailbox=office@example.com"
curl -H "Authorization: Bearer $KEY" \
"https://mail.example.com/mlw-mailbox-api/?r=/v1/messages&mailbox=office@example.com&folder=INBOX&limit=20&offset=0&unseen=1"
Die Nachrichtenliste unterstützt q= für die Suche und unseen=1 nur für ungelesene und kommt neueste zuerst mit uid, subject, from, to, date, size und seen zurück. Eine einzelne Nachricht lesen liefert Text, HTML, Header und Anhänge (Name, Typ, Größe, signierte URL):
curl -H "Authorization: Bearer $KEY" \
"https://mail.example.com/mlw-mailbox-api/?r=/v1/messages/1234&mailbox=office@example.com&folder=INBOX"
Anhänge kommen als signierte Links, die nach 1 Stunde ablaufen und an den API-Schlüssel gebunden sind, der das Postfach lesen darf — du kannst sie also herunterladen, ohne den Schlüssel weiterzugeben. Einen neuen Link bekommst du, indem du die Nachricht erneut über die API liest.
Mit dem Recht organize kannst du als gelesen oder ungelesen markieren, verschieben und löschen:
curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"read":true}' \
"https://mail.example.com/mlw-mailbox-api/?r=/v1/messages/1234/read&mailbox=office@example.com&folder=INBOX"
curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"folder":"Archive"}' \
"https://mail.example.com/mlw-mailbox-api/?r=/v1/messages/1234/move&mailbox=office@example.com&folder=INBOX"
Ein DELETE auf /v1/messages/{uid} entfernt die Nachricht endgültig. Fehler sind JSON mit HTTP-Status: 401 Schlüssel fehlt oder ist falsch, 403 der Schlüssel hat kein Recht auf dieses Postfach, 404 unbekannte Route oder Nachricht, 422 ungültige Eingabe, 429/502 Grenzen oder ein Serverfehler.
Schritt 5 — Als das Postfach senden
Senden braucht das Recht send. Der Body nimmt to, subject und text und unterstützt außerdem cc, bcc, html und reply_to. Eine Kopie landet in Gesendet — das zählt, wenn später jemand das Postfach öffnet und sich fragt, was geantwortet wurde:
curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"to":"customer@example.org","subject":"Your invoice","text":"Attached."}' \
"https://mail.example.com/mlw-mailbox-api/?r=/v1/send&mailbox=office@example.com"
An medialab.works wird nichts gesendet; die Nachricht geht über dein eigenes mailcow raus.
Schritt 6 — Webhooks für neue Mail, verifiziert
In der Karte Monitored mailboxes fügst du ein Postfach, den zu überwachenden Ordner (Standard INBOX), eine https-Webhook-Adresse und optional eine Telegram-Chat-ID hinzu. Das Modul prüft jede Minute auf neue Nachrichten über UIDNEXT je Ordner. Beachte das Design: Der erste Lauf setzt nur den Startpunkt, alte Mail wird also nicht erneut zugestellt — neue Mail ab dann schon.
Jede neue Nachricht erzeugt einen JSON-POST mit event, mailbox, folder, uid, received_at und einem message-Objekt mit message_id, date, from, to, cc, subject, size, text, html, headers und Anhängen (Name, Typ, Größe, sha256 und signierte URL).
Bevor du den Body verwendest, prüfe den Signatur-Header:
X-MLW-Signature: t=1792300000,v1=<hex HMAC-SHA256(secret, "t.body")>
X-MLW-Event: message.received
Jeder Monitor hat sein eigenes Secret, das beim ersten Gebrauch erzeugt wird; die Seite zeigt es beim Anlegen des Monitors und über den Button Secret. Vergleiche den HMAC mit deinem Secret und lehne Zeitstempel ab, die älter als fünf Minuten sind. In n8n zum Beispiel packst du diese Prüfung in einen kleinen Code- oder Function-Node direkt nach dem Webhook-Node, bevor irgendetwas anderes die Payload anfasst.
Fehlgeschlagene Zustellungen werden nach 30 s, 1 min, 2 min … bis zu fünf Versuchen wiederholt. Zustellungen werden in der Datenbank eingereiht und vom Timer im Minutentakt gesendet, und die Karte Recent deliveries zeigt Status und Antwort — deine erste Anlaufstelle, wenn ein Workflow stumm zu sein scheint.
Schritt 7 — Telegram-Benachrichtigungen je Postfach
Füge das Bot-Token unter Settings ein (es wird verschlüsselt gespeichert, nur die letzten vier Zeichen werden angezeigt; ein Bot dient allen Postfächern). Dann setzt du die Chat-ID — ein Nutzer, eine Gruppe oder ein Kanal — auf jedes überwachte Postfach. Der Bot muss Mitglied sein und bei Kanälen Administrator. Telegram neben einem Postfach schickt eine Testnachricht. Die Benachrichtigung zeigt Absender, Betreff und die ersten 400 Zeichen des Textes.
Das passt gut zu einem gemeinsamen Postfach: ein Chat je überwachtem Postfach, und du siehst neue Mail auf dem Handy, ohne einen Mail-Client zu öffnen.
Tipps und Fehlersuche
503 not_licensed: Aktiviere die Lizenz unter System → Updates (Toolkit); ohne Internet läuft das Modul nach der letzten Prüfung noch 7 Tage.502 server_errormit "no dovecot master credentials": mailcow hat/etc/sogo/sieve.credsnicht angelegt; prüfe, ob dovecot-mailcow und sogo-mailcow laufen, oder setze Master-Benutzer und Passwort von Hand.403 not_allowed: Der Schlüssel deckt dieses Postfach nicht ab oder ihm fehlt das Recht; lege in der Admin-Seite einen neuen Schlüssel mit dem passenden Umfang an.- Kein Webhook kommt an: Prüfe, ob der Monitor aktiv ist, die Adresse https ist und mit 2xx antwortet, und schau unter Recent deliveries; der Timer läuft jede Minute (
systemctl status mlcow-mailapi-<name>.timer). - Keine alte Mail nach dem Hinzufügen eines Monitors: Das ist so gewollt, der erste Lauf merkt sich nur die aktuelle Position.
- Halte Public API address auf deine echte https-Adresse gesetzt; sonst zeigen signierte Anhangslinks an die falsche Stelle.
- Die API legt Postfächer nicht an, löscht sie nicht und ändert sie nicht; sie arbeitet nur in den Postfächern, die du erlaubst. Sie liest Mail über den Master-Login von dovecot innerhalb des mailcow-Netzes — derselbe Mechanismus, den mailcow für imapsync nutzt.
Fazit
Die Admin-API verwaltet den Server; dieses Modul verwaltet die Mail, ein Schlüssel mit begrenztem Umfang nach dem anderen. Mit einem read/send/organize-Schlüssel je Postfach, signierten Webhooks in n8n und einem Telegram-Chat für die Postfächer, die dir wichtig sind, bekommst du Automatisierung rund um dein eigenes mailcow, ohne etwas preiszugeben. Fang mit einem Postfach und einem Nur-Lese-Schlüssel an, bestätige, dass die Webhook-Signaturprüfung funktioniert, und erweitere dann den Umfang. Die Bedienungsanleitung enthält die vollständige Routentabelle, das Changelog verfolgt die Updates; wenn etwas nicht zusammenpasst, sind die Wissensdatenbank und die FAQ gute nächste Anlaufstellen, oder kontaktiere uns.