---
title: "VizMail : construire une API email complète avec des agents IA"
description: "Retour d'expérience sur la construction de VizMail — 34 tickets, 642 tests, 0 regressions. Comment des agents IA ont livré une API HTTP email de A à Z."
created: 2026-05-06T19:00:00+02:00
updated: 2026-05-13T16:00:00+02:00
tags: ["Agents IA", "VizMail", "API", "Méthode", "Retex"]
image: /images/blog/vizmail-api-retex-cover.webp
card: /images/blog/vizmail-api-retex-card.webp
---

## Le problème de départ

VizMail est un logiciel desktop MAUI .NET conçu pour un usage précis : classer sémantiquement les emails sans avoir à les trier manuellement. L'idée de départ n'est pas d'exposer une API — c'est d'éliminer la corvée de la boîte de réception.

L'API HTTP est arrivée plus tard, comme une extension naturelle : si un agent IA peut lire et traiter les mails directement, pourquoi forcer l'humain à rester dans la boucle pour les actions répétitives ? Les données — boîte de réception, threads, labels, pièces jointes — vivent dans un processus .NET local. Pour qu'un agent y accède, il faut une surface d'API.

La question n'était pas *si* construire cette API, mais *comment* la construire sans y passer trois semaines.

## La méthode : un ticket, une feature

Chaque fonctionnalité a été découpée en ticket atomique dans [KittyClaw](/projects/kittyclaw). Un ticket = une feature = un endpoint ou une correction. Pas de ticket fourre-tout, pas de "refactoring global".

Le workflow pour chaque ticket :

1. **Programmer** reçoit le ticket, écrit le code C# dans le `LocalHttpServer`
2. **QA-tester** exécute les tests existants, écrit les nouveaux tests, vérifie les régressions
3. **Committer** commit et ouvre la PR
4. **Evaluator** note la qualité du livrable

KittyClaw orchestre cette chaîne automatiquement. Quand le programmer finit, le qa-tester est déclenché. Quand les tests passent, le committer prend la main. Zéro coordination manuelle.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 220" width="100%">
  <defs>
    <pattern id="vmar-art-dots" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
      <circle cx="1" cy="1" r="0.8" fill="#1e2a3a" opacity="0.8"/>
    </pattern>
  </defs>
  <rect width="720" height="220" fill="#0d1117"/>
  <rect width="720" height="220" fill="url(#vmar-art-dots)"/>

  <!-- Ticket -->
  <rect x="20" y="80" width="110" height="48" rx="5" fill="#161b22" stroke="#f0883e" stroke-width="1.5"/>
  <rect x="20" y="80" width="110" height="4" rx="2" fill="#f0883e"/>
  <text x="75" y="104" font-family="monospace" font-size="12" fill="#e6edf3" text-anchor="middle">Ticket</text>
  <text x="75" y="120" font-family="monospace" font-size="10" fill="#8b949e" text-anchor="middle">KittyClaw</text>

  <!-- Arrow -->
  <line x1="130" y1="104" x2="158" y2="104" stroke="#8957e5" stroke-width="1.5" stroke-dasharray="3,2"/>
  <polygon points="154,98 166,104 154,110" fill="#8957e5"/>

  <!-- Programmer -->
  <rect x="166" y="80" width="110" height="48" rx="5" fill="#161b22" stroke="#8957e5" stroke-width="1.5"/>
  <rect x="166" y="80" width="110" height="4" rx="2" fill="#8957e5"/>
  <text x="221" y="104" font-family="monospace" font-size="12" fill="#e6edf3" text-anchor="middle">programmer</text>
  <text x="221" y="120" font-family="monospace" font-size="10" fill="#8b949e" text-anchor="middle">code C#</text>

  <!-- Arrow -->
  <line x1="276" y1="104" x2="304" y2="104" stroke="#8957e5" stroke-width="1.5" stroke-dasharray="3,2"/>
  <polygon points="300,98 312,104 300,110" fill="#8957e5"/>

  <!-- QA -->
  <rect x="312" y="80" width="110" height="48" rx="5" fill="#161b22" stroke="#8957e5" stroke-width="1.5"/>
  <rect x="312" y="80" width="110" height="4" rx="2" fill="#8957e5"/>
  <text x="367" y="104" font-family="monospace" font-size="12" fill="#e6edf3" text-anchor="middle">qa-tester</text>
  <text x="367" y="120" font-family="monospace" font-size="10" fill="#8b949e" text-anchor="middle">tests</text>

  <!-- Arrow -->
  <line x1="422" y1="104" x2="450" y2="104" stroke="#8957e5" stroke-width="1.5" stroke-dasharray="3,2"/>
  <polygon points="446,98 458,104 446,110" fill="#8957e5"/>

  <!-- Committer -->
  <rect x="458" y="80" width="110" height="48" rx="5" fill="#161b22" stroke="#8957e5" stroke-width="1.5"/>
  <rect x="458" y="80" width="110" height="4" rx="2" fill="#8957e5"/>
  <text x="513" y="104" font-family="monospace" font-size="12" fill="#e6edf3" text-anchor="middle">committer</text>
  <text x="513" y="120" font-family="monospace" font-size="10" fill="#8b949e" text-anchor="middle">PR</text>

  <!-- Arrow -->
  <line x1="568" y1="104" x2="596" y2="104" stroke="#30a14e" stroke-width="1.5" stroke-dasharray="3,2"/>
  <polygon points="592,98 604,104 592,110" fill="#30a14e"/>

  <!-- Done -->
  <rect x="604" y="80" width="96" height="48" rx="5" fill="#161b22" stroke="#30a14e" stroke-width="1.5"/>
  <rect x="604" y="80" width="96" height="4" rx="2" fill="#30a14e"/>
  <text x="652" y="108" font-family="monospace" font-size="12" fill="#30a14e" text-anchor="middle">Done ✓</text>

  <!-- Labels -->
  <text x="75" y="164" font-family="monospace" font-size="10" fill="#6e7681" text-anchor="middle">1. trigger</text>
  <text x="221" y="164" font-family="monospace" font-size="10" fill="#6e7681" text-anchor="middle">2. implement</text>
  <text x="367" y="164" font-family="monospace" font-size="10" fill="#6e7681" text-anchor="middle">3. verify</text>
  <text x="513" y="164" font-family="monospace" font-size="10" fill="#6e7681" text-anchor="middle">4. ship</text>
</svg>

## Les chiffres

Trente-quatre tickets livrés. Six cent quarante-deux tests. Zéro regression détectée après merge.

Ce qui frappe, c'est la progression régulière. Du scaffolding initial (ticket #1) aux opérations batch (ticket #34 — `mark_read`/`mark_unread` en lot), chaque itération a posé une brique sans casser la précédente. Le qa-tester a systématiquement rejoué la suite complète à chaque PR — ce n'est pas une affirmation de principe, c'est ce que montre l'historique.

Le temps moyen entre l'ouverture d'un ticket et sa PR ? Moins d'une heure pour les features standard. Les corrections de bugs (tickets #7, #25) ont parfois pris plus de temps, mais pas à cause du code — à cause du diagnostic.

## Les patterns qui ont émergé

Quelques conventions se sont stabilisées au fil des tickets, pas par design initial mais par nécessité.

**Pagination avec `before=`**. Les listings d'emails sans borne peuvent exploser le contexte d'un agent. Le paramètre `before={timestamp}` permet de paginer en remontant dans le temps sans état serveur côté agent. Simple et prévisible.

**Batch max 500**. Les endpoints batch (`/batch/mark_processed`, `/batch/mark_read`) acceptent au maximum 500 IDs par appel. Limite arbitraire au départ, devenue convention — assez grande pour être utile, assez petite pour rester traçable.

**MailState vs SQLite**. `MailState` est la vue en mémoire des emails synchronisés depuis IMAP — rapide, volatile. SQLite est la persistance des métadonnées locales (labels, processed, starred). Un endpoint qui modifie un label écrit dans les deux. Un endpoint qui lit une liste interroge `MailState`. Cette séparation est restée stable du ticket #4 au ticket #34.

**Idempotence des labels**. Ajouter un label déjà présent ne renvoie pas d'erreur. Supprimer un label absent non plus. Les agents appellent sans gérer l'état courant — c'est l'API qui absorbe les doublons.

## Ce que ça change

Le programmer n'a pas "codé" VizMail. Il a exécuté des specs précises dans un environnement contrôlé. La vraie valeur ajoutée était dans la description des tickets — l'angle, les edge cases, le comportement attendu. Une fois la spec claire, l'implémentation était mécanique.

Ce n'est pas une critique, c'est une observation. Le travail intellectuel s'est déplacé vers la conception des tickets. La QA automatisée a capturé les régressions que l'implémentation rapide aurait sinon laissé passer. Et le committer a maintenu l'historique git propre sans intervention humaine.

Trente-quatre features en quelques jours, avec une couverture de tests solide. La méthode fonctionne quand les specs sont honnêtes.

---

*VizMail est un logiciel desktop de gestion d'emails développé par Ekioo. [Découvrir la méthode ekioo →](/about)*
