L'API REST

Tutto ciò che le app Antimatter fanno passa attraverso la stessa API HTTP, quindi anche i tuoi script possono farlo. Questa pagina spiega come autenticarsi, le risorse che userai di più, come appaiono le risposte e gli errori, e come seguire gli eventi in tempo reale tramite WebSocket.

Fondamenti

  • URL di base: https://chat.example.com/api/v4. Tutti gli endpoint seguenti sono relativi a questo.
  • Formato: JSON in input e in output, ad eccezione dei caricamenti di file (multipart). Invia Content-Type: application/json.
  • ID: ogni oggetto ha un id di 26 caratteri. I tempi sono in millisecondi dall'epoca Unix (create_at, update_at, delete_at; 0 significa "non eliminato").
  • Le community sono team: l'API li chiama teams, con un team_id.
  • Te stesso: ovunque un percorso accetti un user_id, me indica l'utente autenticato, come in GET /users/me.

L'elenco completo degli endpoint è nel codice del server: le rotte sono dichiarate in server/channels/api4/*.go e i tipi in server/public/model. La descrizione OpenAPI nella cartella api/v4/source del repository copre gli endpoint ereditati da Mattermost e alcune delle aggiunte di Antimatter: spostamento dei thread, connessioni al modello e azioni sui messaggi. Gli altri sono presenti solo nel codice.

Autenticazione

Ogni richiesta, ad eccezione dell'accesso e di alcuni endpoint pubblici, richiede un token nell'intestazione Authorization:

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

Sono accettati tre tipi di token: token di accesso personale, token di sessione e token di accesso OAuth. È accettato anche Authorization: Token …. I token nella stringa di query (?access_token=) sono accettati solo per i token di accesso OAuth.

Token di accesso personale (consigliati per gli script)

Un token di accesso personale agisce come te, non scade a meno che non gli si assegni una data di scadenza e può essere revocato in qualsiasi momento. I token di accesso personale sono disabilitati di default:

  1. Un amministratore di sistema attiva Attiva Token di Accesso in Console di Sistema › Integrazioni › Gestione integrazioni (ServiceSettings.EnableUserAccessTokens, AM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS).
  2. Per ogni persona che potrebbe creare token, un amministratore di sistema apre Console di Sistema › Gestione utenti › Utenti, seleziona Gestisci ruoli nel menu della persona e spunta Consenti a questo account di generare token di accesso personale. Gli amministratori di sistema possono sempre crearli.
  3. L'utente apre il proprio avatar in alto a destra, quindi Profilo e impostazioni › Sicurezza. Sotto Gestisci Token di Accesso personali, descrive lo scopo del token e sceglie Crea Token. Il token viene mostrato una sola volta: copialo.

Con l'API: POST /users/{user_id}/tokens con {"description": "backup script", "expires_at": 0} (expires_at in millisecondi; 0 per nessuna scadenza). Il campo token della risposta è il segreto; il suo id è quello che si usa per revocarlo (POST /users/tokens/revoke con {"token_id": "…"}). Dalla riga di comando: amctl token generate <user> <description>.

Un amministratore di sistema può impostare un limite alla durata dei token con Durata massima token di accesso personale (giorni) nella stessa pagina della Console di Sistema.

Token di sessione

L'accesso con una password restituisce un token di sessione nell'intestazione di risposta Token, lo stesso tipo di sessione usato dalle app:

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

Aggiungi "token": "123456" per un account con autenticazione a più fattori. Le sessioni scadono come specificato dalle impostazioni di sessione del server, e l'endpoint di accesso è soggetto a limitazione di frequenza (5 richieste al secondo). POST /users/logout termina la sessione.

I browser si autenticano con un cookie. Una richiesta autenticata tramite cookie deve anche inviare l'intestazione X-CSRF-Token per qualsiasi operazione diversa da GET; un token nell'intestazione Authorization non richiede un'intestazione CSRF.

App OAuth 2.0

Per consentire alle persone di accedere alla tua applicazione con il proprio account Antimatter, registra un'app OAuth 2.0. Antimatter diventa quindi il provider OAuth:

  1. Attiva fornitore servizio OAuth 2.0 deve essere attivo (Console di Sistema › Integrazioni › Gestione integrazioni, ServiceSettings.EnableOAuthServiceProvider, attivo per impostazione predefinita).
  2. Chi ha il diritto di gestire le app OAuth (di default gli amministratori di sistema) apre il proprio avatar, quindi Integrazioni › App OAuth 2.0, e aggiunge l'app con i suoi URL di callback. Riceve un client ID e un client secret.
  3. La tua applicazione reindirizza gli utenti a https://chat.example.com/oauth/authorize con response_type=code, client_id, redirect_uri e state. È supportato PKCE con code_challenge_method=S256.
  4. Effettua lo scambio del codice tramite POST https://chat.example.com/oauth/access_token (con grant_type=authorization_code, poi refresh_token per il rinnovo) e chiama l'API con Authorization: Bearer <access token>.

Gli utenti possono visualizzare le app che hanno autorizzato in Profilo e impostazioni › Sicurezza › App autorizzate.

Risorse principali

ElementoEndpoint
Tu e gli altri utentiGET /users/me, GET /users/{user_id}, GET /users/username/{username}, POST /users/ids (una lista di ID), POST /users/search, GET /users?in_team=…&in_channel=…
ComunitàGET /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
CanaliGET /users/me/teams/{team_id}/channels, GET /teams/name/{team}/channels/name/{channel}, POST /channels, POST /channels/direct (due ID utente), POST /channels/group, POST /channels/{channel_id}/members
Messaggi (post)POST /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
ReazioniPOST /reactions con {"user_id", "post_id", "emoji_name"}, DELETE /users/{user_id}/posts/{post_id}/reactions/{emoji_name}
FilePOST /files (multipart: channel_id e files), quindi inserisci gli ID restituiti in file_ids del messaggio; GET /files/{file_id}
PreferenzeGET /users/me/preferences, PUT /users/me/preferences
Integrazioni/hooks/incoming, /hooks/outgoing, /commands, /bots, /oauth/apps (vedi le pagine successive)
ServerGET /system/ping (stato di salute, non richiede token), GET /config/client?format=old (le impostazioni visibili ai client)

Pubblica un messaggio

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"}'

I messaggi sono in Markdown. Aggiungi "root_id" (il primo post di un thread) per rispondere in un thread, "file_ids" per i file e "props" per gli allegati e altre proprietà (vedi Allegati).

Trova l'ID di un canale

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

I nomi sono quelli presenti nel link del canale: /lumen-labs/channels/ops-alerts.

Paginazione

Gli endpoint di elenco accettano page (da 0) e per_page (60 di default, massimo 200). Continua a richiedere la pagina successiva finché una pagina non restituisce meno elementi di per_page.

I messaggi di un canale (GET /channels/{channel_id}/posts) accettano anche since (un tempo: tutto ciò che è cambiato da allora), oppure before e after (un ID di post). Vengono restituiti come un elenco di post: {"order": [IDs, newest first], "posts": {ID: post}, "next_post_id", "prev_post_id"}.

Errori

Gli errori utilizzano i codici di stato HTTP standard (400 input non valido, 401 non autenticato, 403 non autorizzato, 404 non trovato, 501 funzionalità disabilitata) e un corpo JSON:

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

Utilizza id nel tuo codice: message viene tradotto nella lingua dell'utente. detailed_error è vuoto a meno che il server non sia in modalità sviluppatore (ServiceSettings.EnableDeveloper). Cita request_id all'amministratore del server: è presente nei log.

Limiti di frequenza

Il limite di velocità è disattivato per impostazione predefinita. Quando un amministratore di sistema lo attiva (Console di Sistema › Ambiente › Limite di velocità), ogni client riceve 10 richieste al secondo con burst di 100 per impostazione predefinita, conteggiate per indirizzo IP o per utente. Le risposte includono X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset; se si supera il limite, il server risponde con 429 e un header Retry-After in secondi. Alcuni endpoint, come l'accesso, hanno un limite proprio indipendentemente dalle impostazioni.

Scrivi script che attendono e riprovano in caso di 429, ed evita il polling: usa invece il WebSocket.

Il flusso di eventi WebSocket

Connettiti a wss://chat.example.com/api/v4/websocket. Autenticati con l'header Authorization: Bearer nella richiesta di connessione, oppure inviando questo come primo messaggio:

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

Il server invia un evento hello, seguito da un evento per tutto ciò a cui hai accesso:

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

Nota che data.post è il post come stringa JSON. Eventi comuni: posted, post_edited, post_deleted, reaction_added, reaction_removed, typing, status_change, user_added, direct_added. Le app inviano i propri eventi, denominati custom_<app id>_<event>.

Puoi anche inviare azioni, ciascuna con un seq crescente: user_typing con {"channel_id", "parent_id"}, oppure get_statuses. Riconnettiti quando la connessione si interrompe e recupera ciò che hai perso con since (vedi Paginazione).

Antimatter aggiunge questi eventi: community_attributes_updated, member_timeouts_updated, post_put_on_top, post_removed_from_top, server_categories_updated, channel_overrides_updated e category_overrides_updated.

Librerie client

  • Vai: model.Client4 in server/public/model (importa github.com/mattermost/mattermost/server/public/model). model.NewAPIv4Client(url), poi client.SetToken(token); model.NewWebSocketClient4(wsURL, token) segue gli eventi. La copia di Antimatter aggiunge metodi per i propri endpoint, come UpdateMessageRequest, PutPostOnTop e ExportChannel.
  • TypeScript: la classe Client4 di @mattermost/client, in webapp/platform/client, con i relativi tipi in webapp/platform/types. La copia nel repository di Antimatter include gli endpoint di Antimatter; il pacchetto pubblicato su npm da Mattermost non li include.
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)
}

Cosa Antimatter aggiunge all'API

Questi endpoint e campi non esistono su un server Mattermost. Tutti richiedono una sessione.

EndpointFunzione
GET /teams/{team_id}/user_attributes/fields
POST stesso percorso
PATCH, DELETE …/fields/{field_id}
Attributi della community: i campi del profilo definiti dagli amministratori della community per i suoi membri (fino a 20). I membri possono leggerli; gli amministratori della community li creano e modificano.
GET /teams/{team_id}/members/{user_id}/user_attributes
PATCH stesso percorso
POST /teams/{team_id}/user_attributes/values/search
I valori degli attributi di un membro, come {field_id: value}. I membri impostano i propri (tranne quelli gestiti dagli amministratori); gli amministratori impostano quelli di chiunque. La ricerca accetta {"user_ids": [...]} (fino a 200).
GET /teams/{team_id}/timeouts
PUT /teams/{team_id}/members/{user_id}/timeout
GET /teams/{team_id}/members/{user_id}/moderation
Timeout: {"until": time, "reason": "…"} impedisce a qualcuno di pubblicare, reagire e caricare nella community fino a quel momento (al massimo 28 giorni); "until": 0 lo rimuove. La vista di moderazione mostra quando si sono uniti e quanto hanno pubblicato di recente.
GET /channels/{channel_id}/on_top
PUT, DELETE /posts/{post_id}/on_top
Messaggi mantenuti in cima a un canale (fino a 10). {"duration": ms}, al massimo 30 giorni; 0 li mantiene finché non vengono rimossi. Chiunque possa leggere il canale li elenca. Per mettere un messaggio in cima o rimuoverlo è necessario il diritto di pubblicare nel canale e, in un canale di una community, essere amministratore del canale o della community, oppure disporre di Gestisci messaggi (delete_others_posts) lì. In un messaggio diretto o di gruppo, possono farlo i suoi membri. Rifiutato nei canali archiviati.
POST /channels/{channel_id}/message_requestAgisce sulla richiesta di messaggio personale: {"action": "accept"}, "dismiss" o "request" per rimettere una conversazione tra le richieste. Restituisce il tuo membro del canale.
GET, PUT /channels/{channel_id}/overrides
GET /channels/{channel_id}/overrides/effective
GET, PUT /channels/{channel_id}/role_access
Cosa può fare un ruolo o una persona in un canale, e quali ruoli visualizzano un canale protetto da ruolo.
GET, PUT /teams/{team_id}/server_categories
GET, PUT …/server_categories/overrides
Le categorie di canali condivise della community, e le sovrascritture che si applicano a ogni canale in una categoria.
GET /groups/{group_id}/team_role
PUT …/team_role/permissions
Le autorizzazioni concesse da un ruolo della community.
POST /teams/{team_id}/autolinks/previewProva le regole di autolink della community su un testo.
POST /channels/{channel_id}/export?format=csv&since=&until=Scarica i messaggi di un canale in formato CSV (fino a 50.000), per le persone che possono leggere il canale.
GET /posts/{post_id}/speechIl messaggio letto ad alta voce, in formato audio, quando la sintesi vocale è configurata.
POST /posts/{post_id}/move_to_channelSposta un thread in un altro canale della stessa community: vedi Sposta un thread.
GET, POST /ai/connections
GET, PATCH, DELETE /ai/connections/{connection_id}
GET /ai/connections/usable
Connessioni ai modelli, i provider AI utilizzati dalle funzioni AI del server e dalle app: vedi Connessioni ai modelli.
GET /channels/{channel_id}/message_actions
POST /posts/{post_id}/message_actions/{hook_id}
GET, POST /hooks/outgoing/{hook_id}/icon
Azioni sui messaggi: webhook in uscita che le persone eseguono dal menu del messaggio. Vedi Azioni sui messaggi.

Nuovi campi

  • Community (Team, e PUT /teams/{team_id}/patch): all_channels_membership (tutti sono in ogni canale che possono vedere), default_channel_id (dove arrivano i nuovi arrivati; vuoto significa General), kind (community o workplace: in un workplace, gli amministratori dei canali possono archiviare anche i propri canali; le nuove community ottengono il TeamSettings.DefaultCommunityKind del server, community di default), member_list_view (come l'elenco dei membri raggruppa i membri: choice, il default, permette a ciascun membro di scegliere; roles o teams lo fissa), hide_role_colors, require_team_badges, onboarding e autolinks (entrambi stringhe JSON).
  • Il canale predefinito di una nuova community è general (General), non town-square. L'aggiornamento alla versione 0.9.0 rinomina il town-square di ogni community in general, mantenendone l'ID, a meno che la community non disponga già di un canale general. I nomi precedenti continuano a funzionare: GET /teams/{team_id}/channels/name/town-square, GET /teams/name/{team}/channels/name/town-square e il campo channel di un webhook in ingresso restituiscono il canale predefinito della community quando non esiste un canale con quel nome. Utilizza default_channel_id o il nome general nel nuovo codice.
  • Link ai canali: #channel-name collega a un canale, proprio come ~channel-name. La proprietà channel_mentions del post elenca i canali collegati in uno dei due modi; un #word che non corrisponde a un canale resta un hashtag. L'interfaccia web e l'app mobile di Antimatter scrivono #name.
  • I canali archiviati accettano ancora nuovi membri (POST /channels/{channel_id}/members), che possono leggerne la cronologia. Nessuno può pubblicare, reagire, fissare, mettere un messaggio in cima, rinominare il canale o modificare i ruoli dei suoi membri.
  • Risposte inline: un post con "props": {"reply_to": "<post id>"} cita quel messaggio dello stesso canale. Aggiungi "reply_to_mention": false per non notificare l'autore. Il server compila metadata.reply_to con una breve descrizione del messaggio citato.
  • Spoiler: "props": {"spoiler": true} nasconde il testo e i file di un post finché il lettore non sceglie di visualizzarli.
  • Ignorare e bloccare sono preferenze: categoria user_block, denominata in base all'ID dell'altra persona, con il valore ignore o block. Se un messaggio diretto attende come richiesta è definito in notify_props.dm_request del tuo membro del canale (pending, accepted o dismissed).
  • Allegati dei messaggi possono contenere stages e un chart: vedi Cards.

Spostare un thread

POST /posts/{post_id}/move_to_channel sposta un thread, il suo primo messaggio e tutte le sue risposte, in un altro canale della stessa community. post_id può essere il primo messaggio o qualsiasi risposta.

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 (obbligatorio): il canale in cui viene spostato il thread.
  • flatten (valore predefinito false): se impostato su true, le risposte diventano messaggi del canale di destinazione anziché un thread. La funzione Make it a channel di Fusion la utilizza.

I messaggi mantengono i loro autori, i tempi, le reazioni e i file. Le copie hanno la proprietà silent_notification, quindi non notificano nessuno. I messaggi originali vengono eliminati. La risposta è il primo messaggio spostato, con il suo nuovo ID.

Chi può spostare un thread

  • Devi poter leggere il thread e pubblicare nel canale di destinazione.
  • Gli amministratori del canale, gli amministratori della community, le persone con Gestisci messaggi (delete_others_posts) nel canale e gli amministratori di sistema possono spostare qualsiasi thread.
  • L'autore del thread può spostarlo quando entrambi i canali sono pubblici, o quando tutti i messaggi del thread sono suoi. In caso contrario, la risposta è 403 con l'ID api.post.move_thread.private_needs_moderator.app_error.
  • Entrambi i canali devono appartenere alla stessa community (non sono ammessi messaggi diretti o di gruppo), essere diversi e non archiviati. Non è possibile spostare i messaggi di sistema né i messaggi che si autodistruggono alla lettura.
  • Un thread con più di WranglerSettings.MoveThreadMaxCount messaggi (100 di default; 0 per nessun limite) viene rifiutato.

Il puntatore e la nota

Lo spostamento genera due messaggi di sistema di tipo system_wrangler: un puntatore nel canale di origine ("Un thread con 13 messaggi è stato spostato in #target") e una nota nel canale di destinazione ("Questo thread è stato spostato da #source"), posizionati sotto il thread spostato o, con flatten, dopo i suoi messaggi. Oltre al testo del messaggio, le proprietà (props) permettono al client di visualizzare il nome del canale come un collegamento:

  • Il puntatore: MovedThreadPermalink, NumMessages, MovedThreadChannelId, MovedThreadTeamName, MovedThreadPostId e, per una destinazione pubblica, MovedThreadChannelName e MovedThreadChannelDisplayName.
  • La nota: MovedFromChannelId, MovedFromTeamName, MovedFromPostId (il puntatore), MovedFromPermalink e, per un'origine pubblica, MovedFromChannelName e MovedFromChannelDisplayName.

Entrambi hanno anche un TranslationID. Il nome di un canale privato viene omesso: i lettori vedranno "un altro canale". L'endpoint POST /posts/{post_id}/move di Mattermost è disabilitato in Antimatter; usa questo endpoint.

Connessioni ai modelli

Una connessione al modello è l'indirizzo e la chiave di un provider di IA, configurati da un amministratore. Le funzionalità di IA del server e le app che chiamano il servizio di IA li utilizzano. Gli amministratori di sistema gestiscono le connessioni a livello di server (manage_ai_connections); gli amministratori di una community gestiscono le connessioni della propria community (manage_team_ai_connections). Le chiavi sono solo in scrittura: nessuna risposta le contiene, solo api_key_set e api_key_last4.

EndpointFunzione
GET /ai/connections?scope=Le connessioni che gestisci. scope è server o l'ID di una community; se omesso, restituisce tutti gli scope che gestisci.
POST /ai/connectionsAggiunge una connessione (risponde con 201).
POST /ai/connections/test, POST /ai/connections/modelsTesta una connessione o ne elenca i modelli prima di salvarla. Stesso corpo della richiesta di aggiunta; nulla viene memorizzato.
GET, PATCH (o PUT), DELETE /ai/connections/{connection_id}Legge, modifica o elimina una connessione. Una patch contiene solo i campi da modificare; ometti api_key per mantenere la chiave esistente.
POST /ai/connections/{connection_id}/testTesta una connessione salvata: {"ok", "error", "status_code", "latency_ms", "model_count"}.
GET /ai/connections/{connection_id}/modelsI modelli offerti dal provider.
GET /ai/connections/{connection_id}/usageRichieste e token di questo mese.
GET /ai/connections/usable?channel_id=&team_id=Per chiunque sia autenticato: le connessioni che puoi utilizzare, con i relativi modelli e funzionalità, mai le chiavi o gli indirizzi.

Il corpo di una nuova connessione:

{
  "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 è openai_compatible o anthropic. scope ha come valore predefinito server; l'ID di una community la rende una connessione di quella community. In limits, 0 significa nessun limite; le richieste al minuto vengono conteggiate su ogni server, i budget di token per mese solare (UTC) per l'intero cluster. sharing indica quali community possono utilizzare una connessione a livello di server (i messaggi diretti e di gruppo richiedono all_teams), e audience chi può utilizzarla; gli ospiti solo con allow_guests. Le modifiche vengono registrate nel registro di controllo. La guida per gli amministratori spiega le impostazioni: vedi AI.