Înapoi la blog
Scris de Andrei BiroUltima actualizare

Outlook Era Stricat. 25 de Commit-uri Mai Târziu, Funcționa.

Martie 2026

Trimitem un mesaj personal fiecărui client nou pentru a verifica dacă totul funcționează — putem vedea statisticile de clasificare și rezultatele de ingestie din partea noastră, deci știm când ceva nu arată bine înainte să ne spună ei. Pe 18 martie, unul dintre primii noștri clienți ne-a răspuns. Tocmai se abonase la BillyBox și încerca să-și conecteze contul de Outlook. Nu funcționa. Facturile importante ale afacerii ei ajungeau pe Hotmail, iar fără acea conexiune, BillyBox trăgea doar documente din Gmail — care nu era locul unde erau lucrurile importante.

Mesajul ei era simplu: „Mă chinui să-mi integrez conturile de Outlook. Și nu prea trage facturi."

Nu doar testa — facturile afacerii ei depindeau de asta.

Ce a urmat a fost o noapte de construcție, debugging, blocaje, pivoturi și în final rescrierea completă a abordării. Iată povestea reală, spusă prin git log-ul nostru.

Microsoft a Eliminat Accesul cu Parolă

Când am lansat BillyBox, conexiunile Outlook funcționau prin IMAP — același protocol pe care îl suportă Gmail, Zoho și orice alt furnizor de email. Introduci email-ul și parola, și aplicația se conectează. Standard.

Dar Microsoft dezactivase autentificarea de bază pentru toate conturile personale (@outlook.com, @hotmail.com, @live.com). Fără app password, fără login IMAP — nimic. Singura cale era OAuth2: un flux „Conectează-te cu Microsoft" prin pagina lor de login.

Am folosit Claude (asistentul nostru AI de programare) să cercetăm situația — ce exact a depreciat Microsoft, ce scope-uri OAuth sunt necesare pentru IMAP, cum funcționează înregistrarea Azure app pentru conturi personale. Documentația era împrăștiată și contradictorie. Asta s-a dovedit a fi un semn prevestitor.

Decizia

Am primit email-ul la 22:50, marți seara. Aveam două opțiuni: răspundem cu „lucrăm la asta" și livrăm în câteva zile, sau construim acum.

Am ales acum. Era un client plătitor cu o problemă reală. I-am făcut refund imediat, apoi am început să codăm.

Faza 1: Construcția (04:41 - 07:05)

Primul commit a aterizat la 06:41 — 948 de linii de cod nou. Flux OAuth2 complet, autentificare XOAUTH2 pentru IMAP, pagină de callback în frontend, stocare criptată a token-urilor. Claude a ajutat la scaffolding-ul înregistrării Azure app, schimbul de token-uri OAuth2, și mecanismul SASL XOAUTH2 pe care Microsoft îl cere în loc de parole.

06:41Add Microsoft OAuth2 for Outlook/Hotmail IMAP connection— 948 linii, 17 fișiere
06:59Update docs and types for Outlook provider
07:05Use /consumers/ tenant for Microsoft OAuth— Primul zid: /common/ necesită verificare de publisher

Prima surpriză în producție: endpoint-ul OAuth /common/ al Microsoft necesită verificare de publisher (un proces de review de câteva săptămâni). Am schimbat pe /consumers/ — endpoint-ul doar pentru conturi personale care nu necesită verificare.

Faza 2: Deploy și Polish (07:17 - 08:18)

Deploy-ul a relevat propriile probleme. Imaginea Docker Alpine avea o incompatibilitate Chromium care strica pre-renderer-ul. Frontend-ul avea nevoie de rutare SPA pentru URL-ul de callback Microsoft. Layout-ul grilei de provideri se strica pe mobil.

07:17Remove "Coming soon" badges from Outlook OAuth buttons
07:26Fix prerender: retry on Chromium crash + add Microsoft callback SPA route
07:47Pin Alpine 3.21 to fix Chromium/Puppeteer incompatibility
08:01Fix provider grid to show Outlook: 4 columns on desktop, 2 on mobile
08:14Add AI invoice classifier gate with dual backend
08:18Add admin impersonation and fix Outlook button in settings

Da, am livrat și clasificatorul AI de facturi în aceeași sesiune. Când ești în flow la 8 dimineața după o noapte de codat, poți la fel de bine să livrezi totul.

Faza 3: Coșmarul Scope-urilor IMAP (08:35 - 09:53)

Aici s-a complicat. Login-ul OAuth2 funcționa — Microsoft accepta credențialele noastre și returna un token valid. Dar când am încercat să citim efectiv email-urile prin IMAP, eșua. Eroarea: "Command Error. 12" la SELECT INBOX. Autentificarea reușește, dar nu poți deschide nicio cutie poștală.

Am încercat fiecare combinație sugerată de documentația Microsoft. Niciuna n-a mers. 8 commit-uri în 78 de minute — fiecare cu o teorie diferită, fiecare greșită:

08:35Add detailed error logging

Logăm scope-urile token-ului să vedem ce-i greșit

08:45Fix: use outlook.office365.com audience

Teorie: IMAP necesită token-uri Exchange Online — nu

09:07Fix: use Exchange Online audience, not Graph

Nu e disponibil nici pentru app-uri personal-only

09:27Skip folder select after OAuth login

Login merge, SELECT eșuează. "Command Error. 12"

09:53Split auth scopes from token exchange

Ultima încercare: scope-uri diferite pentru consent vs. token

Documentația Microsoft spune că IMAP funcționează cu OAuth2 pentru conturi personale. Listează trei formate diferite de scope-uri în funcție de ce pagină citești. Claude ne-a ajutat să cercetăm fiecare abordare sistematic — audience-uri Exchange Online, scope-uri Graph API, token-uri resource-specific — și am încercat fiecare combinație. Niciuna nu funcționa fiabil.

Problema de bază: serverul IMAP al Microsoft pentru conturi personale respinge silențios comenzile SELECT chiar și cu un token OAuth2 valid. Te poți autentifica, dar nu poți citi email-uri. Documentația lor nu menționează această limitare nicăieri.

Faza 4: Pivotul (10:16)

După 8 încercări eșuate de a face IMAP să funcționeze, am luat decizia: abandonăm IMAP complet. În schimb, folosim Microsoft Graph REST API — o abordare complet diferită unde tragi email-uri prin cereri HTTP în loc de protocolul IMAP.

10:16Switch Outlook from IMAP XOAUTH2 to Microsoft Graph REST API

982 linii noi. Fetch complet de email-uri prin Graph API — listare mesaje, descărcare atașamente, threading. Totul prin HTTP.

Nu a fost o schimbare mică. Am scris un serviciu complet nou de 913 linii (outlook_graph_service.py) care oglindește serviciul nostru Gmail API dar folosește endpoint-uri Microsoft Graph. Scope-uri de auth diferite (Mail.Read în loc de IMAP.AccessAsUser.All), gestionare diferită a token-urilor, parsare diferită a formatului de mesaje. Claude a ajutat la arhitectura serviciului, traducerea pattern-urilor IMAP în echivalente Graph API, și gestionarea modelului de paginare al Microsoft.

A funcționat din prima. Fără probleme de scope-uri, fără coduri de eroare misterioase. Graph API face ce serverul IMAP al Microsoft refuză să facă.

Faza 5: Solidificarea (10:46 - 15:44)

Pivotul a funcționat, dar traficul din producție a relevat cazuri noi:

10:46Fix SQLAlchemy session error during Outlook Graph fetch
10:47Fix mid-fetch db.commit() in IMAP service token rotation
11:01Fix transaction poisoning: use separate session for token rotation

Refresh-ul de token în timpul fetch-ului corupe sesiunea DB

11:05Widen seen_emails.email_uid from 64 to 255 chars

ID-urile Graph sunt mai lungi decât UID-urile IMAP

13:13Fix Outlook showing as "Other IMAP" in settings
14:03Fix TS error: add 'outlook' to EmailAccount provider type
15:44Replace "Connected" badge with checkmark icon

Cel mai urât bug: când token-ul Microsoft expira la mijlocul fetch-ului, apelul de refresh făcea commit în baza de date în interiorul tranzacției de fetch, otrăvind sesiunea SQLAlchemy. Au fost necesare 3 commit-uri să izolăm corect rotația de token-uri într-o sesiune proprie.

Numărătoarea Finală

Total commit-uri:25
Linii de cod:~2.000+
Timp:~11 ore
Încercări eșuate scope IMAP:8
Rescrieri complete:1 (IMAP → Graph API)
Commit-uri asistate de AI:25/25

Rolul AI-ului

Fiecare dintre cele 25 de commit-uri a fost co-autorat cu Claude. Nu ca un generator de cod care scoate boilerplate — ca un partener de cercetare și debugger.

Când am avut nevoie să înțelegem ce a depreciat Microsoft exact, Claude a cercetat. Când am lovit zidul scope-urilor IMAP, Claude ne-a ajutat să încercăm fiecare abordare documentată sistematic. Când am decis să pivotăm pe Graph API, Claude a ajutat la arhitectura unui serviciu de 913 linii dintr-o singură sesiune.

Coșmarul celor 8 commit-uri de scope-uri este un exemplu bun al cum arată de fapt dezvoltarea asistată de AI. Nu e „AI-ul scrie cod perfect." E „AI-ul te ajută să explorezi un spațiu de probleme mai repede." Am încercat 8 abordări în 78 de minute care ar fi durat zile de citit documentație manual. Când niciuna nu a funcționat, aveam suficiente dovezi să luăm decizia de pivot cu încredere.

Refund-ul Care a Păstrat o Clientă

Înainte să începem să construim, i-am făcut refund pentru prima lună. Plătea pentru ceva ce nu funcționa pentru ea încă.

A doua zi dimineața la 11:20, i-am scris: „Outlook este acum suportat." Nu a anulat. E încă abonată azi.

Refund-ul a costat 19,99€. Cele 25 de commit-uri, cele 8 încercări IMAP eșuate, pivotul complet de arhitectură la 10 dimineața — asta e ce trebuie când un client are o problemă și îți pasă.

Conectează-ți Contul Outlook

BillyBox suportă acum conturi Outlook, Hotmail și Live prin Microsoft Graph API. Fără partajare de parole, fără app password-uri, fără configurare IMAP.

Așa construim BillyBox — rapid, aproape de utilizatori, și dispuși să aruncăm codul când nu funcționează.