Passer au contenu principal
JMAP (JSON Meta Application Protocol) est l’API que parle OxiMail pour le courrier, les agendas, les contacts, les tâches, les fichiers et le chat. Cette page couvre la couche Core de la RFC 8620 : comment un client découvre le serveur, s’authentifie, groupe plusieurs appels de méthode dans une seule requête HTTP, enchaîne ces appels avec des références arrière, déclare les capacités qu’il utilise, et reçoit les notifications push. Tout ce qui suit décrit ce que OxiMail expose réellement en v0.30.0. Les jeux de méthodes par domaine s’appuient sur cette couche : voir JMAP Mail, Agendas, Contacts, Tâches, Fichiers, Partage et Chat.

La ressource session

Un client JMAP commence par récupérer la ressource session. C’est le document de découverte unique qui indique au client tout ce dont il a besoin : les capacités prises en charge par le serveur, les comptes accessibles à l’utilisateur, et les URL de toutes les autres opérations.
GET /.well-known/jmap
Authorization: Bearer <token>
Le point d’accès de la session est authentifié : il faut présenter un token valide pour le récupérer. La réponse est un objet JSON (RFC 8620 §2) qui contient les champs suivants.
ChampContenu
capabilitiesLa table des capacités au niveau serveur : chaque URN de capacité associée à son objet de configuration (limites, options).
accountsChaque compte accessible à cet utilisateur, indexé par identifiant de compte. Chaque entrée contient name, isPersonal, isReadOnly et ses propres accountCapabilities.
primaryAccountsPour chaque URN de capacité, l’identifiant du compte considéré comme principal.
usernameLe nom de l’utilisateur authentifié (son adresse e-mail).
apiUrlLe point d’accès où envoyer les appels de méthode : /jmap.
uploadUrlLe modèle d’URL pour l’envoi de blobs : /jmap/upload/{accountId}.
downloadUrlLe modèle d’URL pour le téléchargement de blobs : /jmap/download/{accountId}/{blobId}/{name}?type={type}.
eventSourceUrlLe modèle d’URL pour le push (EventSource) : /jmap/eventsource/?types={types}&closeafter={closeafter}&ping={ping}.
stateUne chaîne opaque qui change dès que l’objet session lui-même change (par exemple lorsqu’un compte est ajouté).
L’objet de la capacité Core annonce aussi les limites strictes du serveur, qu’un client doit respecter avant d’envoyer une requête.
LimiteValeur en v0.30.0
maxSizeUpload50 Mo
maxConcurrentUpload4
maxSizeRequest10 Mo
maxConcurrentRequests8
maxCallsInRequest64
maxObjectsInGet500
maxObjectsInSet500
Un utilisateur peut voir plus d’un compte dans la session. Outre son compte personnel, tout dossier, agenda ou carnet d’adresses partagé avec lui via le partage JMAP apparaît comme une entrée de compte supplémentaire (indexée shared:{ownerId}), qui ne porte que les capacités partagées et parfois isReadOnly: true.

Authentification

OxiMail utilise des tokens Bearer (RFC 6750). On obtient un token en envoyant les identifiants au point d’accès de connexion.
POST /auth/login
Content-Type: application/json

{ "email": "alice@example.com", "password": "..." }
La réponse renvoie le token et l’identifiant du compte.
{ "accessToken": "...", "accountId": "..." }
Envoyez ce token sur chaque requête suivante.
Authorization: Bearer <token>

Durée de vie et renouvellement du token

Les tokens expirent 24 heures après leur émission. Pour rester connecté sans ressaisir de mot de passe, renouvelez le token.
POST /auth/refresh
Authorization: Bearer <token-actuel>
Si le token est encore valide, ou expiré depuis moins de 7 jours, le serveur supprime l’ancien token et en renvoie un nouveau avec une nouvelle expiration à 24 heures. Le format de la réponse est le même que pour la connexion : { "accessToken": "...", "accountId": "..." }. Passé le délai de grâce de 7 jours, le renouvellement échoue avec un 401 et l’utilisateur doit se reconnecter.

EventSource et le paramètre de requête access_token

L’API EventSource du navigateur ne peut pas définir d’en-têtes de requête personnalisés, donc elle ne peut pas envoyer Authorization: Bearer. Pour prendre en charge le push dans le navigateur, OxiMail accepte le token sous forme de paramètre de requête sur le point d’accès EventSource (conformément à la RFC 6750 §2.3).
GET /jmap/eventsource/?types=*&access_token=<token>
C’est le seul point d’accès où le token peut transiter dans l’URL. Partout ailleurs, utilisez l’en-tête Authorization.

Émettre des requêtes

Tous les appels de méthode vont vers un seul point d’accès, dans une unique requête POST groupée. Le corps de la requête (RFC 8620 §3.3) comporte trois parties.
POST /jmap
Authorization: Bearer <token>
Content-Type: application/json
{
  "using": [
    "urn:ietf:params:jmap:core",
    "urn:ietf:params:jmap:mail"
  ],
  "methodCalls": [
    ["Mailbox/get", { "accountId": "a1", "ids": null }, "c0"],
    ["Email/get", { "accountId": "a1", "ids": ["e1", "e2"] }, "c1"]
  ]
}
  • using déclare les capacités sur lesquelles la requête s’appuie. Une méthode dont la capacité n’est pas listée dans using est rejetée avec unknownCapability. La méthode n’est jamais exécutée malgré tout.
  • methodCalls est un tableau ordonné. Chaque appel est un tableau de trois éléments : [nomMéthode, arguments, callId]. Le callId est votre propre étiquette, renvoyée telle quelle pour que vous puissiez associer chaque réponse à son appel.
La réponse reprend cette structure avec un tableau methodResponses, dans le même ordre, chaque élément étant étiqueté avec le callId correspondant.
{
  "methodResponses": [
    ["Mailbox/get", { "accountId": "a1", "state": "...", "list": [ ... ] }, "c0"],
    ["Email/get", { "accountId": "a1", "state": "...", "list": [ ... ], "notFound": [] }, "c1"]
  ]
}
Un identifiant invalide ne disparaît jamais en silence. Tout ce qu’un appel /get ne trouve pas revient dans notFound. OxiMail n’abandonne jamais discrètement un identifiant illisible ou inconnu.

Références arrière (result references)

L’intérêt du groupage, c’est qu’un appel peut alimenter le suivant dans la même requête, ce qui évite un aller-retour. C’est une référence arrière, aussi appelée result reference (RFC 8620 §3.7). À la place d’une valeur d’argument littérale, vous passez un objet avec un préfixe # sur le nom de l’argument.
{
  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
  "methodCalls": [
    ["Email/query", { "accountId": "a1", "filter": { "inMailbox": "inbox" } }, "c0"],
    ["Email/get", {
      "accountId": "a1",
      "#ids": {
        "resultOf": "c0",
        "name": "Email/query",
        "path": "/ids"
      }
    }, "c1"]
  ]
}
Le second appel signifie : « pour mon argument ids, prends le résultat de l’appel c0 (qui doit être un Email/query) et récupère la valeur au pointeur JSON /ids ». Le serveur résout la référence à partir du résultat du premier appel avant d’exécuter le second. Une référence arrière doit indiquer le bon callId précédent, le bon name de méthode et un path valide. Si l’un de ces éléments est faux, OxiMail renvoie invalidResultReference au lieu de substituer null ou une liste vide.

Capacités

Le serveur annonce ce qu’il sait faire via des URN de capacité dans la table capabilities de la session. Le client déclare ensuite celles qu’il compte utiliser en les listant dans le tableau using de la requête. OxiMail annonce deux familles.

JMAP standard (v1)

Les capacités normalisées par l’IETF, dans l’espace de noms urn:ietf:params:jmap:* : Core, Mail (RFC 8621), Submission, réponse d’absence, Sieve (RFC 9661), Quota (RFC 9425), Principals et partage (RFC 9670), Contacts (RFC 9610), Agendas, Fichiers et WebSocket (RFC 8887).

JMAP modernisé OxiMail (v2)

OxiMail expose aussi un ensemble de capacités modernisées dans l’espace de noms urn:oximail:params:jmap:v2:*. Ce sont les extensions JMAP modernisées d’OxiMail (Internet-Drafts en cours), qui couvrent notamment :
URN de capacitéCe qu’elle ajoute
urn:oximail:params:jmap:v2:mailUne surface mail modernisée (import, envoi, annulation, réindexation) posée sur les méthodes Email classiques.
urn:oximail:params:jmap:v2:threadsLe type Thread de première classe, avec fusion et convergence.
urn:oximail:params:jmap:v2:labelsDes labels plats et partageables, au lieu de dossiers imbriqués.
urn:oximail:params:jmap:v2:rulesDes règles de filtrage typées, avec une garantie de temps d’exécution par règle.
urn:oximail:params:jmap:v2:calendarDes règles de récurrence et des participants de première classe.
urn:oximail:params:jmap:v2:contactsUn modèle de contacts modernisé, avec une table de mutabilité documentée.
urn:oximail:params:jmap:v2:streamingLa reprise du push et la prise en charge des requêtes en flux.
urn:oximail:params:jmap:v2:resource-limitsDes limites de conformité annoncées, qu’un client peut vérifier avant d’envoyer.
Voir JMAP v2 modernisé pour la surface complète.

Mélanger v1 et v2 dans une même requête

En v0.30.0, les surfaces mail, contacts et agenda v1 et v2 sont servies par les mêmes gestionnaires, grâce à un mécanisme de double capacité : un gestionnaire qui annonce une capacité v1 comme capacité principale accepte aussi la capacité v2 correspondante comme capacité additionnelle. Une requête peut donc lister à la fois une capacité v1 et son équivalent v2 dans using en même temps. La structure renvoyée sur le réseau est l’union des propriétés v1 et v2, et le client lit celle qu’il veut via l’argument properties de /get.
Le protocole conserve une vérification d’exclusion mutuelle pour un usage futur, mais en v0.30.0 l’ensemble des paires v1/v2 mutuellement exclusives est vide. Rien n’est rejeté lorsqu’on associe une capacité v1 à son équivalent v2. Si une future surface v3 réintroduisait une séparation stricte, une requête qui associerait les deux URN exclus serait rejetée avec unknownCapability.

Push : savoir quand l’état change

Chaque type JMAP suit une chaîne d’état opaque. Quand vous appelez Foo/get, la réponse inclut l’état courant (state). Plus tard, vous pouvez appeler Foo/changes avec le dernier état que vous avez vu, et le serveur vous indique exactement quels objets ont été créés, mis à jour ou détruits depuis. Vous ne sondez jamais des listes complètes : vous synchronisez des deltas. Pour savoir quand appeler Foo/changes, OxiMail envoie une petite notification StateChange dès que l’état d’un type avance. Il existe deux transports, tous deux annoncés dans la session.

EventSource (Server-Sent Events)

Un flux SSE standard à l’adresse eventSourceUrl.
GET /jmap/eventsource/?types=*&access_token=<token>
Le serveur diffuse des événements StateChange. Chaque événement nomme le compte et la collection dont l’état a bougé, ainsi que la nouvelle chaîne d’état. Utilisez types=Email,Mailbox pour filtrer sur certaines collections, ping= pour l’intervalle de keep-alive, et access_token= pour l’authentification dans le navigateur (voir plus haut). Le flux est limité au compte et à l’organisation authentifiés : vous ne recevez que vos propres changements.

WebSocket

OxiMail parle aussi JMAP par-dessus WebSocket (RFC 8887). La session annonce une URL wss://.../jmap/ws avec supportsPush: true. Un client qui détient déjà une connexion WebSocket peut donc à la fois envoyer des appels de méthode et recevoir le push sur le même canal. Dans les deux cas, le principe est le même : une notification push est un indice qu’une collection a changé d’état. Le client enchaîne avec Foo/changes pour récupérer le delta réel.

Pour aller plus loin