Zurück zum Blog
Geschrieben von Andrei BiroZuletzt aktualisiert

Outlook war kaputt. 25 Commits später funktionierte es.

März 2026

Wir schreiben jeden neuen Kunden persönlich an, um zu prüfen, ob alles funktioniert — wir sehen Klassifizierungsstatistiken und Abrufergebnisse auf unserer Seite, sodass wir wissen, wenn etwas nicht stimmt, bevor sie es uns überhaupt mitteilen. Am 18. März antwortete eine unserer ersten Kundinnen. Sie hatte gerade BillyBox abonniert und versuchte, ihr Outlook-Konto zu verbinden. Es funktionierte nicht. Ihre wichtigsten Geschäftsrechnungen gingen an Hotmail, und ohne diese Verbindung zog BillyBox nur Dokumente aus ihrem Gmail — wo die wichtigen Sachen nicht waren.

Ihre Nachricht war einfach: „Ich habe Schwierigkeiten, meine Outlook-Konten zu integrieren. Außerdem stellt es fest, dass es nicht wirklich Rechnungen abruft."

Sie testete nicht nur — ihre Geschäftsrechnungen hingen davon ab.

Was folgte, war eine Nacht des Bauens, Debuggens, Anrennens gegen Wände, Umschwenkens und letztlich des kompletten Neuschreibens des gesamten Ansatzes. Hier ist die wahre Geschichte, erzählt durch unser Git-Log.

Microsoft hat passwortbasierten Zugang abgeschafft

Als wir BillyBox gestartet haben, funktionierten Outlook-Verbindungen über IMAP — dasselbe Protokoll, das Gmail, Zoho und jeder andere E-Mail-Anbieter unterstützt. Du gibst deine E-Mail-Adresse und dein Passwort ein, und die App verbindet sich, um deine Nachrichten zu lesen. Standard-Vorgehen.

Aber Microsoft hatte leise die Basisauthentifizierung für alle persönlichen Konten deaktiviert (@outlook.com, @hotmail.com, @live.com). Kein App-Passwort, kein IMAP-Login — nichts. Der einzige Weg rein war OAuth2: ein „Mit Microsoft anmelden"- Flow, der über Microsofts eigene Anmeldeseite läuft.

Wir nutzten Claude (unseren KI-Programmierassistenten), um die Situation zu recherchieren — was genau Microsoft eingestellt hat, welche OAuth-Scopes für IMAP benötigt werden, wie die Azure-App-Registrierung für persönliche Konten funktioniert. Die Dokumentation war verstreut und widersprüchlich. Das stellte sich als Vorzeichen heraus.

Die Entscheidung

Wir bekamen ihre E-Mail um 22:50 Uhr an einem Dienstagabend. Wir hatten zwei Optionen: mit „wir arbeiten daran" antworten und in ein paar Tagen liefern, oder es jetzt sofort bauen.

Wir wählten „jetzt sofort". Sie war eine zahlende Kundin mit einem echten Problem. Wir erstatteten ihr sofort den ersten Monat und fingen an zu programmieren.

Phase 1: Der Aufbau (6:41 - 7:05 Uhr)

Der erste Commit landete um 6:41 Uhr — 948 Zeilen neuer Code. Vollständiger OAuth2-Flow, XOAUTH2-IMAP-Authentifizierung, Frontend-Callback-Seite, verschlüsselte Token-Speicherung. Claude half beim Aufsetzen der Azure-App-Registrierung, dem OAuth2-Token-Austausch und dem SASL-XOAUTH2-Mechanismus, den Microsoft statt Passwörtern verlangt.

06:41Microsoft OAuth2 für Outlook/Hotmail IMAP-Verbindung hinzufügen— 948 Zeilen, 17 Dateien
06:59Docs und Typen für Outlook-Provider aktualisieren
07:05/consumers/-Tenant für Microsoft OAuth verwenden— Erste Wand: /common/ erfordert Publisher-Verifizierung

Erste Produktionsüberraschung: Microsofts /common/-OAuth-Endpunkt erfordert eine Publisher-Verifizierung (ein mehrwöchiger Überprüfungsprozess). Wir wechselten zu /consumers/ — dem Endpunkt nur für persönliche Konten, der das nicht erfordert.

Phase 2: Deploy und Feinschliff (7:17 - 8:18 Uhr)

Das Deployment offenbarte eigene Probleme. Das Docker-Alpine-Image hatte eine Chromium-Inkompatibilität, die unseren Prerenderer brach. Das Frontend brauchte SPA-Routing für die Microsoft-Callback-URL. Das Provider-Grid-Layout ging auf Mobilgeräten kaputt.

07:17„Demnächst"-Badges von Outlook-OAuth-Buttons entfernen
07:26Prerender reparieren: Retry bei Chromium-Absturz + Microsoft-Callback-SPA-Route
07:47Alpine 3.21 pinnen für Chromium/Puppeteer-Kompatibilität
08:01Provider-Grid für Outlook: 4 Spalten Desktop, 2 Spalten Mobil
08:14KI-Rechnungsklassifizierer-Gate mit dualem Backend hinzufügen
08:18Admin-Impersonation und Outlook-Button in Einstellungen hinzufügen

Ja, wir haben den KI-Rechnungsklassifizierer in derselben Session ausgeliefert. Wenn du um 8 Uhr morgens im Flow bist, nachdem du die ganze Nacht durchprogrammiert hast, kannst du auch gleich alles ausliefern.

Phase 3: Der IMAP-Scope-Albtraum (8:35 - 9:53 Uhr)

Hier wurde es hässlich. OAuth2-Login funktionierte — Microsoft akzeptierte unsere Anmeldedaten und gab ein gültiges Token zurück. Aber als wir versuchten, tatsächlich E-Mails per IMAP zu lesen, schlug es fehl. Der Fehler: "Command Error. 12" bei SELECT INBOX. Authentifizierung erfolgreich, aber du kannst kein Postfach öffnen.

Wir probierten jede Kombination, die Microsofts Dokumentation vorschlug. Keine funktionierte. 8 Commits in 78 Minuten — jeder eine andere Theorie, jede falsch:

08:35Detailliertes Error-Logging für Outlook-IMAP-Testfehler

Token-Scopes loggen, um herauszufinden, was falsch ist

08:45Fix: outlook.office365.com-Audience verwenden

Theorie: IMAP braucht Exchange-Online-Tokens — nein

09:07Fix: Exchange-Online-Audience statt Graph verwenden

Auch nicht verfügbar für Personal-Only-Apps

09:27Ordner-Select nach OAuth-Login überspringen

Login funktioniert, SELECT scheitert. "Command Error. 12"

09:53Auth-Scopes von Token-Exchange-Scopes trennen

Letzter Versuch: verschiedene Scopes für Consent vs. Token

Microsofts Dokumentation sagt, IMAP funktioniert mit OAuth2 für persönliche Konten. Sie listet drei verschiedene Scope-Formate, je nachdem welche Seite man liest. Claude half uns, jeden Ansatz zu recherchieren — Exchange-Online-Audiences, Graph-API-Scopes, ressourcenspezifische Tokens — und wir probierten jede Kombination. Keine funktionierte zuverlässig.

Das Kernproblem: Microsofts IMAP-Server für persönliche Konten lehnt SELECT-Befehle stillschweigend ab, selbst mit einem gültigen OAuth2-Token. Du kannst dich authentifizieren, aber du kannst keine Mails lesen. Ihre Dokumentation erwähnt diese Einschränkung nirgends.

Phase 4: Der Pivot (10:16 Uhr)

Nach 8 gescheiterten Versuchen, IMAP zum Laufen zu bringen, trafen wir die Entscheidung: IMAP komplett aufgeben. Stattdessen Microsofts Graph-REST-API verwenden — ein völlig anderer Ansatz, bei dem E-Mails per HTTP-Requests statt per IMAP-Protokoll abgerufen werden.

10:16Outlook von IMAP XOAUTH2 auf Microsoft Graph REST API umstellen

982 neue Zeilen. Vollständiger Graph-API-Mail-Abruf — Nachrichtenlisten, Anhang-Downloads, Message-Threading. Alles per HTTP.

Das war keine kleine Änderung. Wir schrieben einen komplett neuen 913-Zeilen-Service (outlook_graph_service.py), der unseren Gmail-API-Service spiegelt, aber Microsoft-Graph-Endpunkte nutzt. Verschiedene Auth-Scopes (Mail.Read statt IMAP.AccessAsUser.All), anderes Token-Handling, anderes Nachrichtenformat-Parsing. Claude half bei der Architektur des Services, der Übersetzung unserer IMAP-Muster in Graph-API-Äquivalente und dem Handling von Microsofts Paginierungsmodell.

Es funktionierte beim ersten Versuch. Keine Scope-Probleme, keine mysteriösen Fehlercodes. Die Graph-API tut, was Microsofts IMAP-Server sich weigert zu tun.

Phase 5: Stabilisierung (10:46 - 15:44 Uhr)

Der Pivot funktionierte, aber Produktions-Traffic offenbarte neue Grenzfälle:

10:46SQLAlchemy-Session-Fehler bei Outlook-Graph-Fetch beheben
10:47Mittendrin-db.commit() im IMAP-Service Token-Rotation beheben
11:01Transaction-Poisoning beheben: separate Session für Token-Rotation

Token-Refresh während E-Mail-Fetch korrumpierte die DB-Session

11:05seen_emails.email_uid von 64 auf 255 Zeichen erweitern

Graph-Message-IDs sind länger als IMAP-UIDs

13:13Outlook-Anzeige als "Other IMAP" in Einstellungen korrigieren
14:03TS-Fehler beheben: 'outlook' zum EmailAccount-Provider-Typ hinzufügen
15:44„Verbunden"-Badge durch Häkchen-Icon ersetzen

Der fieseste Bug: Wenn Microsofts Token mitten im Fetch ablief, führte der Refresh-Aufruf ein Commit in der Datenbank innerhalb der Fetch-Transaktion durch und vergiftete die SQLAlchemy-Session. Es brauchte 3 Commits, um die Token-Rotation sauber in eine eigene Session zu isolieren.

Die Bilanz

Commits insgesamt:25
Codezeilen:~2.000+
Zeit:~11 Stunden
Gescheiterte IMAP-Scope-Versuche:8
Komplette Neuschreibungen:1 (IMAP → Graph API)
KI-unterstützte Commits:25/25

Die Rolle der KI

Jeder dieser 25 Commits wurde zusammen mit Claude geschrieben. Nicht als Code-Generator, der Boilerplate ausspuckt — als Recherche-Partner und Debugger.

Als wir verstehen mussten, was Microsoft tatsächlich eingestellt hatte, recherchierte Claude. Als wir an die IMAP-Scope-Wand stießen, half Claude uns, jeden dokumentierten Ansatz systematisch durchzuprobieren. Als wir uns für den Pivot zur Graph-API entschieden, half Claude bei der Architektur eines 913-Zeilen-Services in einer Sitzung — übersetzte unsere IMAP-Muster in REST-Äquivalente, handhabte Microsofts Paginierungsmodell und mappte deren Nachrichtenformat auf unser Schema.

Der 8-Commit-Scope-Albtraum ist ein gutes Beispiel dafür, wie KI-unterstützte Entwicklung tatsächlich aussieht. Es ist nicht „KI schreibt perfekten Code." Es ist „KI hilft dir, einen Problemraum schneller zu erforschen." Wir haben 8 Ansätze in 78 Minuten durchprobiert, die bei manuellem Dokumentationslesen Tage gedauert hätten. Als keiner funktionierte, hatten wir genug Evidenz, um die Pivot-Entscheidung sicher zu treffen.

Die Erstattung, die eine Kundin hielt

Bevor wir anfingen zu bauen, erstatteten wir ihr den ersten Monat. Sie bezahlte für etwas, das für sie noch nicht funktionierte.

Am nächsten Morgen um 11:20 Uhr schrieben wir ihr: „Outlook wird jetzt unterstützt." Sie kündigte nicht. Sie ist heute noch Abonnentin.

Die Erstattung kostete 19,99 EUR. Die 25 Commits, die 8 gescheiterten IMAP-Versuche, der komplette Architektur-Pivot um 10 Uhr morgens — das ist einfach das, was es braucht, wenn ein Kunde ein Problem hat und man sich wirklich darum kümmert.

Verbinde dein Outlook-Konto

BillyBox unterstützt jetzt Outlook-, Hotmail- und Live-Konten über die Microsoft Graph API. Kein Passwort teilen, keine App-Passwörter, keine IMAP-Konfiguration.

So bauen wir BillyBox — schnell, nah am Nutzer und bereit, Code wegzuwerfen, wenn er nicht funktioniert.