API
JSON over HTTP, /api prefix, session cookie. /docs, /redoc and /openapi.json are disabled.
POST /api/login {password};POST /api/logout;GET /api/mePOST /api/password {current, new}, change the admin password (new≥ 8 characters). Requires a valid session and a correctcurrent. Returns 409 if theADMIN_PASSWORDenv var is set (the env var always wins, so the password cannot be changed through the API). On success, the new hash is stored indata/admin.json, all other sessions are invalidated and a fresh session cookie is returned for the caller.- Servers:
GET/POST /api/servers,GET/PUT/DELETE /api/servers/{id},POST /api/servers/{id}/testServer ={id,name,imap_host,imap_port,imap_security:'ssl'|'starttls'|'none',verify_tls,root_folder:str|null,caldav_url:str|null,carddav_url:str|null,dav_enabled:bool,workers:int,notes,tuning:'auto'|'manual',builders:int|null}(manual:workersIMAP connections,buildersbuilder processes, null = machine default;tuningomitted on creation =manualifworkersis not 2 or 4 (earlier defaults) elseauto, omitted on update = unchanged) (caldav_url/carddav_url: empty =.well-knownauto-discovery, otherwise a template with{email},{user},{domain}) - Accounts:
GET /api/accounts?server_id=,POST /api/accounts {server_id,email,password,login?},PUT/DELETE /api/accounts/{id},POST /api/accounts/{id}/test→{imap:{ok,msg,quota},caldav:{ok,msg},carddav:{ok,msg}},POST /api/accounts/import-csv(multipart file + server_id; columnsemail;password[;login],,or;, optional header) →{created,updated,errors:[{line,msg}]};GET /api/accounts/csv-template. Account ={id,server_id,email,login,has_password,status:'new'|'ok'|'auth_failed'|'error',last_test,pst_id|null,options:{mail:bool,calendar:bool,contacts:bool},job:{...}|null} - PST upload (resumable):
POST /api/uploads {filename,size,last_modified}→{id,chunk_size,received:[n...]}(same file = same upload, resume)PUT /api/uploads/{id}/chunks/{n}binary body,X-Chunk-Sha256header → 200 / 422 if wrong hashGET /api/uploads/{id}→{id,filename,size,chunk_size,received:[...],status}POST /api/uploads/{id}/complete→ starts validation (signature + scan);GET /api/psts,GET /api/psts/{id},DELETE /api/psts/{id};GET /api/psts/{id}/preview?limit=20(limit 1..200) → zip (.eml/.ics/.vcf per folder, sample spread across mails/appointments/contacts) generated by the engine in a subprocess (app.server.preview, 120 s timeout); 404 unknown, 409 if PST notvalid, 500{detail:"crash libpff pendant l'aperçu"}if the subprocess dies; PST ={id,filename,size,status:'uploading'|'validating'|'valid'|'invalid',scan:{...},error}PUT /api/accounts/{id}/pst {pst_id} - Jobs:
POST /api/runs {account_ids:[...]}(queues),POST /api/jobs/{id}/pause|resume|stop|retry,GET /api/jobs?active=1,GET /api/jobs/{id}→{id,account_id,status:'queued'|'running'|'paused'|'stopping'|'stopped'|'done'|'done_with_errors'|'failed',phase:'precheck'|'mail'|'calendar'|'contacts'|'verify',progress:{mail:{done,total,errors},calendar:{...},contacts:{...}},current_folder,rate,eta,started_at,finished_at,error,tuning:{mode,builders,connections,fallback}|null}GET /api/jobs/{id}/log?after=(lines),GET /api/jobs/{id}/report(JSON),GET /api/jobs/{id}/errors,POST /api/jobs/{id}/retry-errors - Real time:
GET /api/events(SSE) emitsjob/upload/pst/accountwith the full object. GET /api/health.