> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oximail.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# Courrier

> La surface JMAP Mail (RFC 8621) : Mailbox, Email, Thread, SearchSnippet, Identity, EmailSubmission, VacationResponse, MDN. Les méthodes, les extensions propres à OxiMail et les comportements à connaître.

Le courrier est la surface de la [RFC 8621](https://www.rfc-editor.org/rfc/rfc8621), demandée avec `urn:ietf:params:jmap:mail` (plus `submission` et `vacationresponse` pour l'envoi et la réponse automatique). Cette page liste ce qu'OxiMail sert et où il va au-delà de la RFC. La mécanique commune à toutes les méthodes (regroupement, références arrière, `/changes`, push) est sur [JMAP Core](./jmap-core).

## Objets et méthodes

| Objet              | Méthodes                                                                                                | Notes                                                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Mailbox`          | `get`, `set`, `query`, `queryChanges`, `changes`                                                        | Les dossiers, avec rôles, hiérarchie, compteurs par dossier et droits `shareWith`.                                                                                                           |
| `Email`            | `get`, `set`, `query`, `queryChanges`, `changes`, `import`, `parse`                                     | L'objet message ; voir les comportements plus bas.                                                                                                                                           |
| `Thread`           | `get`, `changes` (capacité courrier) ; `query`, `queryChanges`, `set`, `archive`, `merge`, `split` (v2) | L'agrégat de conversation, enfilé par Message-ID stocké (chaîne exacte, jamais un hachage).                                                                                                  |
| `SearchSnippet`    | `get`                                                                                                   | Les extraits surlignés pour les résultats de recherche.                                                                                                                                      |
| `Identity`         | `get`, `set`, `changes`, `queryChanges`                                                                 | Les identités d'expéditeur ; les alias sont des identités d'envoi (ADR-089).                                                                                                                 |
| `EmailSubmission`  | `get`, `set`, `query`, `changes`, `queryChanges`                                                        | Les demandes d'envoi, `onSuccessUpdateEmail`, l'envoi différé.                                                                                                                               |
| `VacationResponse` | `get`, `set`                                                                                            | Le singleton de réponse automatique.                                                                                                                                                         |
| `MDN`              | `send`, `parse`                                                                                         | Les accusés de lecture ([RFC 9007](https://www.rfc-editor.org/rfc/rfc9007)), demandés avec `urn:ietf:params:jmap:mdn` : voir [les accusés de lecture](#les-accusés-de-lecture-mdn) plus bas. |
| `Quota`            | `get`                                                                                                   | La visibilité du quota de stockage.                                                                                                                                                          |
| `Preferences`      | `get`, `set`                                                                                            | Les préférences client stockées côté serveur (extension OxiMail, singleton par compte).                                                                                                      |

Extensions de méthode propres à OxiMail sur la surface courrier : **`Email/send`** (l'envoi en un appel), **`Email/cancel`** (annuler un envoi différé ou en fenêtre d'annulation) et **`Email/wake`** (l'expiration d'une mise en veille). La réindexation est une affaire d'administration, pas de client.

## Arbre du corps et valeurs de corps

Tout ce que `Email/get` dit du corps d'un message vient d'**un seul parcours en profondeur du véritable arbre MIME**. C'est ce qui fait que `partId` désigne la même partie dans toutes les propriétés d'une même réponse : l'identifiant lu dans `bodyStructure` est celui que porte la pièce jointe, et c'est la clé à chercher dans `bodyValues`.

| Propriété               | Ce que vous obtenez                                                                                                                                                                                                                                                        |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bodyStructure`         | L'arbre lui-même, récursif. `partId` vaut `null` **si et seulement si** la partie est un `multipart/*` ; chaque feuille en porte un. Une partie `message/rfc822` est une feuille, cohérence faite avec [la règle des pièces jointes](#comportements-à-connaître) plus bas. |
| `textBody` / `htmlBody` | Les parties texte et HTML primaires (premier Text, premier Html). Le `type` annoncé est le vrai type de média de la partie.                                                                                                                                                |
| `bodyValues`            | Le contenu décodé de **toutes** les feuilles `text/*`, jeu de caractères appliqué : `text/calendar`, `text/csv` et `text/vcard` compris, pas seulement le texte brut et le HTML.                                                                                           |
| `attachments`           | La même sélection qu'avant, chaque entrée portant désormais `partId`, `charset` et `disposition`.                                                                                                                                                                          |

<Warning>
  **`bodyValues` est vide si vous ne le demandez pas.** La RFC 8621 §4.2 conditionne le contenu à `fetchTextBodyValues`, `fetchHTMLBodyValues` ou `fetchAllBodyValues`, et OxiMail en tient désormais la lettre : les valeurs étaient auparavant remplies que les arguments soient présents ou non. Un client qui n'a jamais posé d'argument `fetch*` et lisait quand même `bodyValues` y lira une table vide. Posez l'argument. `Email/get` et `Email/parse` passent par la même sélection : la règle est identique des deux côtés.
</Warning>

**Les en-têtes par partie sont le chemin vers les paramètres de Content-Type.** La propriété `type` est normativement nue, `text/calendar` et rien de plus, si bien que les paramètres qui portent le sens ne s'atteignent qu'en demandant les en-têtes de la partie : `bodyProperties: ["partId", "type", "headers"]`. C'est la façon prévue par la RFC de distinguer une véritable invitation (`method=REQUEST`) d'un simple `.ics` joint à la main, **sans télécharger le blob**.

Deux écarts sont délibérés et méritent d'être connus :

* **La projection par défaut retire les en-têtes de partie.** La forme par défaut reste un sous-ensemble de la liste normative du §4.2 ; demandez `headers` explicitement et vous les obtenez.
* **`partId` et `subParts` survivent à une projection explicite dans l'arbre.** Un arbre sans ses branches n'est plus un arbre : omettre `subParts` de `bodyProperties` se lit comme une erreur et non comme un souhait d'aplatir, et le serveur les conserve.

Deux formes ne portent aucun `bodyStructure`, par construction : un message que l'analyseur a refusé et qu'il a relu à plat, car il n'annonce jamais une structure qu'il n'a pas analysée, et un brouillon natif, dont la forme canonique v2 est `body`/`html`.

## Comportements à connaître

* **`notFound` n'est jamais vide à tort.** Chaque identifiant inconnu ou inanalysable d'un `/get` atterrit explicitement dans `notFound`.
* **Les valeurs de `updated` sont `null`.** Conformément à la RFC 8620 §5.3, testez l'appartenance (`id in updated`), pas la véracité de la valeur.
* **`hasAttachment` veut dire pièces jointes.** Le drapeau est calculé depuis la structure MIME à l'ingestion, pas depuis l'extraction de texte.
* **Un message joint est une pièce jointe, pas son contenu.** La liste `attachments` est tirée de la structure de corps de *ce* message (RFC 8621 §4.1.7) : une partie `message/rfc822` **est** la pièce jointe, et ses propres parties ne sont pas annoncées à côté d'elle. Transférer un courrier qui portait lui-même `contract.pdf` liste le `.eml`, pas `contract.pdf` : on ne dit jamais au lecteur qu'il a reçu des fichiers que l'expéditeur n'a pas joints. Les parties encapsulées restent atteignables : analysez le blob `.eml` avec `Email/parse` et leurs blobs se téléchargent normalement, car toutes les parties sont stockées, imbriquées comprises. Ce que la liste annonce et ce qui est téléchargeable sont deux questions distinctes.
* **`Thread/get` et `Thread/changes` ne demandent que la capacité courrier.** La RFC 8621 §3 rend cette paire obligatoire : ces deux méthodes portent donc les deux capacités, `urn:ietf:params:jmap:mail` en primaire et `v2:threads` en additionnelle, et un client qui ne déclare que `core` + `mail` est servi. Les verbes de fil plus riches (`query`, `queryChanges`, `set`, `archive`, `merge`, `split`) restent des extensions v2. La forme `{id, emailIds}` de la RFC est un sous-ensemble strict de ce que sert l'objet : sélectionnez-la avec `properties`.
* **La pagination par ancre est exacte.** `anchor` + `anchorOffset` + `position` dans `Email/query` suivent la RFC 8620 §5.5 à la lettre, positions négatives comprises.
* **Les brouillons se réécrivent.** Une mise à jour du corps d'un message `$draft` fonctionne par le cycle standard destroy+create ; le serveur ne rejette pas l'édition des brouillons.
* **S'écrire à soi-même fonctionne.** `onSuccessUpdateEmail` s'exécute après la remise, et la déduplication n'opère jamais au niveau du message.
* **L'envoi différé est de premier rang.** Une `EmailSubmission` avec une heure d'envoi future est mise en file et annulable (`Email/cancel`) jusqu'à son départ ; les envois différés d'un compte désactivé sont annulés à la source.
* **La rigueur est une fonctionnalité.** `accountId` inconnu donne `accountNotFound` ; une capacité absente de `using` donne `unknownCapability` ; une mauvaise référence arrière donne `invalidResultReference`. Le serveur est l'implémentation de référence et n'a pas de mode tolérant.

## Filtrage et recherche

`Email/query` prend en charge l'arbre de filtre de la RFC 8621 (`inMailbox`, `from`/`to`/`subject`/`text`, `hasKeyword`, `hasAttachment`, dates, tailles) avec les combinateurs `AND`/`OR`/`NOT`, et les conditions plein texte interrogent l'index Tantivy : un message est cherchable dès que SMTP a répondu `250 OK` (accepté veut dire cherchable). `SearchSnippet/get` renvoie les fragments surlignés pour les listes de résultats.

## Mots-clés et drapeaux

Les mots-clés JMAP (`$seen`, `$flagged`, `$draft`, `$answered`, `$junk`, personnalisés) sont le même état que les drapeaux IMAP lisent et écrivent : un seul stockage, deux protocoles. Le pipeline anti-spam classe avec `$junk` ; les étiquettes utilisateur sont un objet v2 distinct ([JMAP v2](./jmap-v2)).

## L'envoi

Le pipeline de soumission valide le `from` contre les identités du compte (enveloppe **et** en-tête : falsifier l'adresse d'un collègue échoue avec `forbiddenFrom`), stocke le message chiffré et le remet à la [file de remise](../operator/mail-and-smtp). `onSuccessUpdateEmail`/`onSuccessDestroyEmail` s'exécutent **après** l'acceptation de la remise, dans la réponse de la même requête.

## Les accusés de lecture (MDN)

Les notifications de disposition de message sont la surface de la RFC 9007, demandée avec `urn:ietf:params:jmap:mdn`. Les deux sens sont servis.

**Lire les accusés qu'on vous a envoyés.** Les MDN entrants sont rattachés, à l'ingestion SMTP, au message envoyé auquel ils répondent, appariés sur `Original-Message-ID` et exposés en `Email.mdnBlobIds`. `MDN/parse` transforme un tel blob en objet `MDN` structuré, avec `disposition`, `originalMessageId`, `finalRecipient`, `reportingUA`, `originalRecipient`, `mdnGateway`, `error` et `extensionFields`. Un client affiche donc « votre message a été affiché le … » sans réimplémenter lui-même la RFC 8098. Les verdicts sont rendus **par blob**, jamais effondrés au niveau de la méthode : chaque identifiant atterrit dans `parsed`, dans `notParsable` (le blob s'analyse comme un courrier mais n'est pas un MDN, ou ne s'analyse pas du tout, la RFC 9007 ne trace pas de ligne plus fine) ou dans `notFound`. Les groupes vides sont servis comme `null`, la forme de la RFC elle-même.

**En envoyer un.** `MDN/send` compose un accusé RFC 8098 pour un message reçu et le remet à la file de sortie, avec `disposition` et, en option, `subject`, `textBody`, `reportingUA`, `finalRecipient` (qui doit être une adresse que ce compte a le droit d'utiliser, sinon `forbiddenFrom`), `includeOriginalMessage` et `extensionFields`.

Deux propriétés de cette méthode méritent d'être connues avant de bâtir dessus :

* **Rien n'envoie de MDN automatiquement.** L'émission est un geste explicite de l'utilisateur, et cette méthode *est* ce geste. L'accusé part avec une enveloppe d'expéditeur nulle (RFC 8098 §3) et ne génère pas de trafic de suivi s'il échoue.
* **Le « déjà envoyé » n'est pas effaçable par le client.** La RFC 9007 ancre le contrôle anti-doublon sur le mot-clé `$mdnsent`, et OxiMail tient la lettre de la RFC : le patch `onSuccessUpdateEmail` **doit** poser `keywords/$mdnsent: true`, refusé nommément sinon, pour qu'un client tiers littéral fonctionne sans modification. Mais le serveur vérifie *en plus* un horodatage d'audit côté serveur, écrit atomiquement avec le patch. Effacer `$mdnsent` ne réarme pas l'envoi.

Pour la vue protocoles classiques des mêmes données, voir [protocoles historiques](../operator/legacy-protocols) ; pour les extensions courrier v2 (étiquettes, mise en veille, messages enregistrés), voir [JMAP v2](./jmap-v2).
