L'API REST

Tout ce que les applications Antimatter font passe par la même API HTTP, vos scripts peuvent donc en faire autant. Cette page explique comment s'authentifier, les ressources que vous utiliserez le plus, l'aspect des réponses et des erreurs, et comment suivre les événements en direct via WebSocket.

Bases

  • URL de base : https://chat.example.com/api/v4. Tous les points de terminaison ci-dessous sont relatifs à celle-ci.
  • Format : JSON en entrée et en sortie, à l'exception des envois de fichiers (multipart). Envoyez Content-Type: application/json.
  • Identifiants : chaque objet possède un id de 26 caractères. Les dates sont en millisecondes depuis l'époque Unix (create_at, update_at, delete_at ; 0 signifie « non supprimé »).
  • Les communautés sont des équipes : l'API les appelle teams, avec un team_id.
  • Vous-même : partout où un chemin prend un user_id, me désigne l'utilisateur connecté, comme dans GET /users/me.

La liste complète des points d'entrée se trouve dans le code du serveur : les routes sont déclarées dans server/channels/api4/*.go et les types dans server/public/model. La description OpenAPI du dossier api/v4/source du dépôt couvre les points d'entrée hérités de Mattermost, ainsi que certaines des ajouts d'Antimatter : déplacement des fils de discussion, connexions de modèles et actions sur les messages. Les autres ne sont disponibles que dans le code.

Authentification

Chaque requête, à l'exception de la connexion et de quelques points de terminaison publics, nécessite un jeton dans l'en-tête Authorization :

curl -H "Authorization: Bearer $TOKEN" https://chat.example.com/api/v4/users/me

Trois types de jetons y sont acceptés : jetons d'accès personnels, jetons de session et jetons d'accès OAuth. Authorization: Token … est également accepté. Les jetons dans la chaîne de requête (?access_token=) ne sont acceptés que pour les jetons d'accès OAuth.

Jetons d'accès personnels (recommandés pour les scripts)

Un jeton d'accès personnel agit en votre nom, n'expire pas à moins que vous ne lui donniez une date d'expiration, et peut être révoqué à tout moment. Les jetons d'accès personnels sont désactivés par défaut :

  1. Un admin système active Activer les jetons d'accès personnel dans Console système › Intégrations › Gestion des intégrations (ServiceSettings.EnableUserAccessTokens, AM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS).
  2. Pour chaque personne pouvant créer des jetons, un administrateur système ouvre Console système › Gestion des utilisateurs › Utilisateurs, choisit Gérer les rôles dans le menu de la personne et coche Permettre à ce compte de générer des jetons d'accès personnels. Les administrateurs système peuvent toujours les créer.
  3. La personne ouvre son avatar en haut à droite, puis Profil et paramètres › Sécurité. Sous Jetons d'accès personnel, elle décrit l'usage du jeton et choisit Créer un nouveau jeton. Le jeton n'est affiché qu'une seule fois : copiez-le.

Via l'API : POST /users/{user_id}/tokens avec {"description": "backup script", "expires_at": 0} (expires_at en millisecondes ; 0 pour aucune). Le champ token de la réponse est le secret ; son id est ce que vous utilisez pour le révoquer (POST /users/tokens/revoke avec {"token_id": "…"}). Depuis la ligne de commande : amctl token generate <user> <description>.

Un admin système peut limiter la durée de validité des jetons avec Durée de validité maximale des jetons d’accès personnels (jours) sur la même page de la Console système.

Jetons de session

La connexion avec un mot de passe renvoie un jeton de session dans l'en-tête de réponse Token, le même type de session utilisé par les applications :

curl -i -X POST https://chat.example.com/api/v4/users/login \
  -H 'Content-Type: application/json' \
  -d '{"login_id": "maya", "password": "…"}'

Ajoutez "token": "123456" pour un compte avec authentification multifacteur. Les sessions expirent selon les paramètres de session du serveur, et le point de terminaison de connexion est limité en débit (5 requêtes par seconde). POST /users/logout met fin à la session.

Les navigateurs s'authentifient avec un cookie. Une requête authentifiée par le cookie doit également envoyer l'en-tête X-CSRF-Token pour tout ce qui n'est pas une requête GET ; un jeton dans l'en-tête Authorization n'a pas besoin d'en-tête CSRF.

Applications OAuth 2.0

Pour permettre aux personnes de se connecter à votre application avec leur compte Antimatter, enregistrez une application OAuth 2.0. Antimatter est alors le fournisseur OAuth :

  1. Activer le fournisseur de service OAuth 2.0 doit être activé (Console système › Intégrations › Gestion des intégrations, ServiceSettings.EnableOAuthServiceProvider, activé par défaut).
  2. Une personne ayant le droit de gérer les applications OAuth (les administrateurs système par défaut) ouvre son avatar, puis Intégrations › Applications OAuth 2.0, et ajoute l'application avec ses URL de rappel. Elle obtient un identifiant de client et un secret de client.
  3. Votre application redirige les utilisateurs vers https://chat.example.com/oauth/authorize avec response_type=code, client_id, redirect_uri et state. PKCE avec code_challenge_method=S256 est pris en charge.
  4. Elle échange le code via POST https://chat.example.com/oauth/access_token (grant_type=authorization_code, puis refresh_token pour le renouveler), et appelle l'API avec Authorization: Bearer <access token>.

Les utilisateurs voient les applications qu'ils ont autorisées dans Profil et paramètres › Sécurité › Applications autorisées.

Ressources principales

ÉlémentPoints d'accès
Vous et les autres utilisateursGET /users/me, GET /users/{user_id}, GET /users/username/{username}, POST /users/ids (une liste d'identifiants), POST /users/search, GET /users?in_team=…&in_channel=…
CommunautésGET /users/me/teams, GET /teams/{team_id}, GET /teams/name/{name}, PUT /teams/{team_id}/patch, GET /teams/{team_id}/members, POST /teams/{team_id}/members
CanauxGET /users/me/teams/{team_id}/channels, GET /teams/name/{team}/channels/name/{channel}, POST /channels, POST /channels/direct (deux identifiants d'utilisateur), POST /channels/group, POST /channels/{channel_id}/members
MessagesPOST /posts, GET /posts/{post_id}, PUT /posts/{post_id}/patch, DELETE /posts/{post_id}, GET /channels/{channel_id}/posts, GET /posts/{post_id}/thread, POST /teams/{team_id}/posts/search
RéactionsPOST /reactions avec {"user_id", "post_id", "emoji_name"}, DELETE /users/{user_id}/posts/{post_id}/reactions/{emoji_name}
FichiersPOST /files (multipart : channel_id et files), puis placez les identifiants renvoyés dans file_ids du message ; GET /files/{file_id}
PréférencesGET /users/me/preferences, PUT /users/me/preferences
Intégrations/hooks/incoming, /hooks/outgoing, /commands, /bots, /oauth/apps (voir les pages suivantes)
ServeurGET /system/ping (état de santé, aucun jeton requis), GET /config/client?format=old (les paramètres visibles par les clients)

Publier un message

curl -X POST https://chat.example.com/api/v4/posts \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"channel_id": "'$CHANNEL_ID'", "message": "Backup finished: **42 GB** in 3 min"}'

Les messages sont au format Markdown. Ajoutez "root_id" (le premier message d'un fil de discussion) pour répondre dans un fil, "file_ids" pour les fichiers et "props" pour les pièces jointes et autres éléments supplémentaires (voir Pièces jointes).

Trouver l'identifiant d'un canal

curl -H "Authorization: Bearer $TOKEN" \
  https://chat.example.com/api/v4/teams/name/lumen-labs/channels/name/ops-alerts

Les noms sont ceux présents dans le lien du canal : /lumen-labs/channels/ops-alerts.

Pagination

Les points d'accès de liste acceptent page (à partir de 0) et per_page (60 par défaut, 200 au maximum). Continuez à demander la page suivante jusqu'à ce qu'une page renvoyée soit plus courte que per_page.

Les messages d'un canal (GET /channels/{channel_id}/posts) acceptent également since (un horodatage : tout ce qui a changé depuis) ou before et after (un identifiant de message). Ils sont renvoyés sous forme de liste de messages : {"order": [IDs, newest first], "posts": {ID: post}, "next_post_id", "prev_post_id"}.

Erreurs

Les erreurs utilisent les codes d'état HTTP habituels (400 entrée invalide, 401 non authentifié, 403 non autorisé, 404 introuvable, 501 fonction désactivée) et un corps JSON :

{
  "id": "api.context.permissions.app_error",
  "message": "You do not have the appropriate permissions.",
  "detailed_error": "",
  "request_id": "jz8zm5nunpbt8f3gpqsz5dq6so",
  "status_code": 403
}

Utilisez id dans votre code : message est traduit dans la langue de l'utilisateur. detailed_error est vide sauf si le serveur fonctionne en mode développeur (ServiceSettings.EnableDeveloper). Citez request_id à l'admin du serveur : il figure dans les journaux.

Limites de débit

La limitation de débit est désactivée par défaut. Lorsqu'un admin système l'active (Console système › Environnement › Limitation de débit), chaque client reçoit 10 requêtes par seconde avec des rafales de 100 par défaut, comptées par adresse IP ou par utilisateur. Les réponses contiennent X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset ; au-delà de la limite, le serveur répond 429 avec un en-tête Retry-After en secondes. Certains points d'accès, comme l'authentification, ont leur propre limite quelle que soit la configuration.

Écrivez des scripts qui attendent et réessaient en cas de 429, et évitez le sondage : suivez plutôt le WebSocket.

Le flux d'événements WebSocket

Connectez-vous à wss://chat.example.com/api/v4/websocket. Authentifiez-vous soit avec l'en-tête Authorization: Bearer sur la requête de connexion, soit en envoyant ceci comme premier message :

{"seq": 1, "action": "authentication_challenge", "data": {"token": "…"}}

Le serveur envoie un événement hello, puis un événement pour tout ce que vous êtes autorisé à voir :

{
  "event": "posted",
  "data": {"channel_id": "…", "channel_type": "O", "post": "{\"id\":\"…\",\"message\":\"Hello\",…}"},
  "broadcast": {"channel_id": "…", "team_id": "", "user_id": "", "omit_users": null},
  "seq": 4
}

Notez que data.post est le message au format JSON. Événements courants : posted, post_edited, post_deleted, reaction_added, reaction_removed, typing, status_change, user_added, direct_added. Les applications émettent leurs propres événements, nommés custom_<app id>_<event>.

Vous pouvez également envoyer des actions, chacune avec un seq croissant : user_typing avec {"channel_id", "parent_id"}, ou get_statuses. Reconnectez-vous lorsque la connexion est interrompue, et récupérez ce que vous avez manqué avec since (voir Pagination).

Antimatter ajoute les événements suivants : community_attributes_updated, member_timeouts_updated, post_put_on_top, post_removed_from_top, server_categories_updated, channel_overrides_updated et category_overrides_updated.

Bibliothèques client

  • Aller à : model.Client4 dans server/public/model (importer github.com/mattermost/mattermost/server/public/model). model.NewAPIv4Client(url), puis client.SetToken(token) ; model.NewWebSocketClient4(wsURL, token) suit les événements. La copie d'Antimatter ajoute des méthodes pour ses propres points d'entrée, telles que UpdateMessageRequest, PutPostOnTop et ExportChannel.
  • TypeScript : la classe Client4 de @mattermost/client, dans webapp/platform/client, avec ses types dans webapp/platform/types. La copie dans le dépôt d'Antimatter inclut les points de terminaison d'Antimatter ; le package publié sur npm par Mattermost ne les inclut pas.
package main

import (
	"context"
	"log"

	"github.com/mattermost/mattermost/server/public/model"
)

func main() {
	client := model.NewAPIv4Client("https://chat.example.com")
	client.SetToken("…")
	post, _, err := client.CreatePost(context.Background(), &model.Post{
		ChannelId: "…",
		Message:   "Hello from Go",
	})
	if err != nil {
		log.Fatal(err)
	}
	log.Println("posted", post.Id)
}

Ce qu'Antimatter ajoute à l'API

Ces points de terminaison et champs n'existent pas sur un serveur Mattermost. Tous nécessitent une session.

Point de terminaisonFonction
GET /teams/{team_id}/user_attributes/fields
POST même chemin
PATCH, DELETE …/fields/{field_id}
Attributs de la communauté : les champs de profil définis par les administrateurs d'une communauté pour ses membres (jusqu'à 20). Les membres peuvent les lire ; les administrateurs de la communauté les créent et les modifient.
GET /teams/{team_id}/members/{user_id}/user_attributes
PATCH même chemin
POST /teams/{team_id}/user_attributes/values/search
Les valeurs d'attributs d'un membre, sous la forme {field_id: value}. Les membres définissent les leurs (sauf ceux gérés par les administrateurs) ; les administrateurs définissent celles de n'importe qui. La recherche prend {"user_ids": [...]} (jusqu'à 200).
GET /teams/{team_id}/timeouts
PUT /teams/{team_id}/members/{user_id}/timeout
GET /teams/{team_id}/members/{user_id}/moderation
Délais d'attente : {"until": time, "reason": "…"} empêche quelqu'un de publier, réagir et envoyer des fichiers dans la communauté jusqu'à cette date (au maximum 28 jours) ; "until": 0 lève la restriction. La vue de modération indique quand ils ont rejoint et combien ils ont publié récemment.
GET /channels/{channel_id}/on_top
PUT, DELETE /posts/{post_id}/on_top
Messages maintenus en haut d'un canal (jusqu'à 10). {"duration": ms}, au maximum 30 jours ; 0 les maintient jusqu'à leur suppression. Toute personne pouvant lire le canal peut les lister. Pour épingler un message en haut ou le retirer, il faut le droit de publier dans le canal et, dans un canal de communauté, d'être administrateur du canal ou de la communauté, ou d'avoir Gérer les messages (delete_others_posts) sur ce canal. Dans un message personnel ou de groupe, ses membres peuvent le faire. Refusé dans les canaux archivés.
POST /channels/{channel_id}/message_requestAgit sur votre propre demande de message : {"action": "accept"}, "dismiss" ou "request" pour remettre une conversation parmi les demandes. Retourne votre membre de canal.
GET, PUT /channels/{channel_id}/overrides
GET /channels/{channel_id}/overrides/effective
GET, PUT /channels/{channel_id}/role_access
Ce qu'un rôle ou une personne peut faire dans un canal, et les rôles qui voient un canal restreint par rôle.
GET, PUT /teams/{team_id}/server_categories
GET, PUT …/server_categories/overrides
Les catégories de canaux partagées par la communauté, ainsi que les modifications qui s'appliquent à tous les canaux d'une catégorie.
GET /groups/{group_id}/team_role
PUT …/team_role/permissions
Les autorisations accordées par un rôle de la communauté.
POST /teams/{team_id}/autolinks/previewTeste les règles de liens automatiques de la communauté sur un texte.
POST /channels/{channel_id}/export?format=csv&since=&until=Télécharge les messages d'un canal au format CSV (jusqu'à 50 000), pour les personnes pouvant lire le canal.
GET /posts/{post_id}/speechLe message lu à voix haute, sous forme audio, lorsque la synthèse vocale est configurée.
POST /posts/{post_id}/move_to_channelDéplace un fil de discussion vers un autre canal de la même communauté : voir Déplacer un fil de discussion.
GET, POST /ai/connections
GET, PATCH, DELETE /ai/connections/{connection_id}
GET /ai/connections/usable
Connexions de modèles, les fournisseurs d'IA utilisés par les fonctionnalités d'IA du serveur et les applications : voir Connexions de modèles.
GET /channels/{channel_id}/message_actions
POST /posts/{post_id}/message_actions/{hook_id}
GET, POST /hooks/outgoing/{hook_id}/icon
Actions sur les messages : webhooks sortants que les utilisateurs déclenchent depuis le menu du message. Voir Actions sur les messages.

Nouveaux champs

  • Communautés (Team, et PUT /teams/{team_id}/patch) : all_channels_membership (tout le monde est membre de chaque canal qu'il peut voir), default_channel_id (où atterrissent les nouveaux arrivants ; vide signifie Général), kind (community ou workplace : dans un espace de travail, les administrateurs de canal peuvent aussi archiver leurs canaux ; les nouvelles communautés reçoivent le TeamSettings.DefaultCommunityKind du serveur, community par défaut), member_list_view (comment la liste des membres regroupe les membres : choice, la valeur par défaut, permet à chaque membre de choisir ; roles ou teams fixe le regroupement), hide_role_colors, require_team_badges, onboarding et autolinks (les deux sont des chaînes JSON).
  • Le canal par défaut d'une nouvelle communauté est general (Général), et non town-square. La mise à niveau 0.9.0 renomme le canal town-square de chaque communauté en general, en conservant son identifiant, à moins que la communauté possède déjà un canal general. Les anciens noms continuent de fonctionner : GET /teams/{team_id}/channels/name/town-square, GET /teams/name/{team}/channels/name/town-square et le paramètre channel d'un webhook entrant renvoient le canal par défaut de la communauté lorsqu'aucun canal ne porte ce nom. Utilisez default_channel_id, ou le nom general, dans le nouveau code.
  • Liaisons de canaux : #channel-name lie un canal, tout comme ~channel-name. La prop channel_mentions de la publication liste les canaux liés de l'une ou l'autre façon ; un #word qui ne nomme aucun canal reste un mot-dièse. L'interface web et l'application mobile d'Antimatter écrivent #name.
  • Les canaux archivés peuvent encore accueillir de nouveaux membres (POST /channels/{channel_id}/members), qui peuvent consulter leur historique. Personne ne peut y publier, réagir, épingler, mettre un message en avant, renommer le canal ou modifier les rôles de ses membres.
  • Réponses en ligne : un message avec "props": {"reply_to": "<post id>"} cite ce message du même canal. Ajoutez "reply_to_mention": false pour ne pas notifier son auteur. Le serveur remplit metadata.reply_to avec une brève description du message cité.
  • Spoilers : "props": {"spoiler": true} masque le texte et les fichiers d'un message jusqu'à ce que le lecteur choisisse de les voir.
  • Ignorer et bloquer sont des préférences : catégorie user_block, nommée d'après l'identifiant de l'autre personne, avec la valeur ignore ou block. Savoir si un message personnel attend en tant que demande se trouve dans notify_props.dm_request de votre membre de canal (pending, accepted ou dismissed).
  • Les pièces jointes de messages peuvent contenir des stages et un chart : voir Cartes.

Déplacer un fil

POST /posts/{post_id}/move_to_channel déplace un fil de discussion, son premier message et toutes ses réponses, vers un autre canal de la même communauté. post_id peut être le premier message ou n'importe laquelle des réponses.

curl -X POST https://chat.example.com/api/v4/posts/$POST_ID/move_to_channel \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"channel_id": "'$TARGET_CHANNEL_ID'", "flatten": false}'
  • channel_id (obligatoire) : le canal de destination du fil de discussion.
  • flatten (valeur par défaut false) : la valeur true transforme les réponses en messages du canal de destination, au lieu de former un fil de discussion. La fonction Make it a channel de Fusion l'utilise.

Les messages conservent leurs auteurs, horodatages, réactions et fichiers. Leurs copies portent la propriété silent_notification, de sorte qu'elles ne génèrent aucune notification. Les messages d'origine sont supprimés. La réponse est le premier message déplacé, avec son nouvel identifiant.

Qui peut déplacer un fil de discussion

  • Vous devez pouvoir lire le fil de discussion et publier dans le canal de destination.
  • Les administrateurs du canal, les administrateurs de la communauté, les personnes disposant de la permission Gérer les messages (delete_others_posts) dans le canal, ainsi que les administrateurs système, peuvent déplacer n'importe quel fil de discussion.
  • L'auteur du fil de discussion peut le déplacer lorsque les deux canaux sont publics, ou lorsque tous les messages du fil sont les siens. Sinon, la réponse est 403 avec l'identifiant api.post.move_thread.private_needs_moderator.app_error.
  • Les deux canaux doivent appartenir à la même communauté (pas de messages personnels ou de groupe), être différents et non archivés. Les messages système et les messages effacés après lecture ne peuvent pas être déplacés.
  • Un fil de discussion contenant plus de WranglerSettings.MoveThreadMaxCount messages (100 par défaut ; 0 pour aucune limite) est refusé.

Le pointeur et la note

Le déplacement laisse deux messages système de type system_wrangler : un pointeur dans l'ancien canal (« Un fil de discussion avec 13 messages a été déplacé vers #target ») et une note dans la cible (« Ce fil de discussion a été déplacé depuis #source »), sous le fil de discussion déplacé ou, avec flatten, après ses messages. Outre le texte du message, leurs propriétés permettent à un client d'afficher le nom du canal sous forme de lien :

  • Le pointeur : MovedThreadPermalink, NumMessages, MovedThreadChannelId, MovedThreadTeamName, MovedThreadPostId et, pour une cible publique, MovedThreadChannelName et MovedThreadChannelDisplayName.
  • La note : MovedFromChannelId, MovedFromTeamName, MovedFromPostId (le pointeur), MovedFromPermalink et, pour une source publique, MovedFromChannelName et MovedFromChannelDisplayName.

Les deux ont également un TranslationID. Le nom d'un canal privé est omis : les lecteurs voient « un autre canal ». L'endpoint POST /posts/{post_id}/move de Mattermost reste désactivé dans Antimatter ; utilisez cet endpoint.

Connexions aux modèles

Une connexion de modèle est l'adresse et la clé d'un fournisseur d'IA, configurées par un admin. Les fonctionnalités IA du serveur et les applications qui appellent le service IA les utilisent. Les admins système gèrent les connexions au niveau du serveur (manage_ai_connections) ; les admins d'une communauté gèrent celles de leur communauté (manage_team_ai_connections). Les clés sont en écriture seule : aucune réponse ne les contient, seulement api_key_set et api_key_last4.

Point de terminaisonFonction
GET /ai/connections?scope=Les connexions que vous gérez. scope est server ou l'identifiant d'une communauté ; sans cela, tous les scopes que vous gérez.
POST /ai/connectionsAjoute une connexion (réponse 201).
POST /ai/connections/test, POST /ai/connections/modelsTeste une connexion, ou liste ses modèles, avant qu'elle ne soit enregistrée. Même corps que pour l'ajout ; rien n'est stocké.
GET, PATCH (ou PUT), DELETE /ai/connections/{connection_id}Lit, modifie ou supprime une connexion. Un patch ne contient que les champs à modifier ; omettez api_key pour conserver la clé.
POST /ai/connections/{connection_id}/testTeste une connexion enregistrée : {"ok", "error", "status_code", "latency_ms", "model_count"}.
GET /ai/connections/{connection_id}/modelsLes modèles proposés par le fournisseur.
GET /ai/connections/{connection_id}/usageLes requêtes et jetons de ce mois-ci.
GET /ai/connections/usable?channel_id=&team_id=Pour toute personne connectée : les connexions que vous pouvez y utiliser, avec leurs modèles et leurs capacités, mais jamais leurs clés ou leurs adresses.

Le corps d'une nouvelle connexion :

{
  "name": "Local models",
  "scope": "server",
  "provider": "openai_compatible",
  "base_url": "http://llm.internal:8000/v1",
  "api_key": "…",
  "extra_headers": {},
  "models": {"chat": "qwen3-32b", "embeddings": "", "transcription": "", "decisions": ""},
  "limits": {"requests_per_minute": 0, "monthly_token_budget": 0, "per_user_requests_per_minute": 0, "per_user_monthly_token_budget": 0},
  "sharing": {"all_teams": true, "team_ids": []},
  "audience": {"everyone": true, "group_ids": [], "roles": [], "allow_guests": false}
}

provider est openai_compatible ou anthropic. scope est server par défaut ; l'identifiant d'une communauté en fait une connexion propre à cette communauté. Dans limits, 0 signifie aucune limite ; les requêtes par minute sont comptées sur chaque serveur, et les budgets de jetons par mois civil (UTC) pour l'ensemble du cluster. sharing indique quelles communautés peuvent utiliser une connexion à l'échelle du serveur (les messages personnels et de groupe nécessitent all_teams), et audience qui peut l'utiliser ; les invités uniquement avec allow_guests. Les modifications sont inscrites dans le journal d'audit. Le guide d'administration explique les paramètres : voir IA.