La API REST
Todo lo que hacen las aplicaciones de Antimatter pasa por la misma API HTTP, por lo que tus scripts también pueden hacerlo. Esta página explica cómo autenticarse, los recursos que más usarás, cómo son las respuestas y los errores, y cómo seguir los eventos en vivo a través de WebSocket.
Básicos
- URL base:
https://chat.example.com/api/v4. Todos los puntos finales siguientes son relativos a esta. - Formato: JSON de entrada y salida, excepto las subidas de archivos (multipart). Envía
Content-Type: application/json. - Identificadores: cada objeto tiene un
idde 26 caracteres. Los tiempos son milisegundos desde la época Unix (create_at,update_at,delete_at; 0 significa «no eliminado»). - Las comunidades son equipos: la API los llama
teams, con unteam_id. - Tú mismo: donde quiera que una ruta acepte un
user_id,mesignifica el usuario autenticado, como enGET /users/me.
La lista completa de puntos de acceso está en el código del servidor: las rutas se declaran en server/channels/api4/*.go y los tipos en server/public/model. La descripción OpenAPI en la carpeta api/v4/source del repositorio cubre los puntos de acceso heredados de Mattermost y algunas de las adiciones de Antimatter: mover hilos, conexiones de modelos, agentes, reglas de la comunidad, modo de concentración y acciones de mensajes. Los demás solo están en el código.
Autenticación
Cada solicitud, excepto el inicio de sesión y algunos puntos finales públicos, requiere un token en la cabecera Authorization:
curl -H "Authorization: Bearer $TOKEN" https://chat.example.com/api/v4/users/me
Se aceptan tres tipos de token allí: tokens de acceso personal, tokens de sesión y tokens de acceso OAuth. También se acepta Authorization: Token …. Los tokens en la cadena de consulta (?access_token=) solo se aceptan para tokens de acceso OAuth.
Tokens de acceso personal (recomendados para scripts)
Un token de acceso personal actúa como tú, no caduca a menos que le asignes una fecha de expiración, y puede revocarse en cualquier momento. Los tokens de acceso personal están desactivados por defecto:
- Un administrador del sistema habilita Habilitar Tokens de Acceso Personales en Consola de sistema › Integraciones › Gestión de Integraciones (
ServiceSettings.EnableUserAccessTokens,AM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS). - Para cada persona que pueda crear tokens, un administrador del sistema abre Consola de sistema › Gestión de Usuarios › Usuarios, elige Gestionar roles en el menú de la persona y marca Permitir que esta cuenta genere tokens de acceso personal. Los administradores del sistema siempre pueden crearlos.
- La persona abre su avatar en la parte superior derecha y luego Perfil y configuración › Seguridad. Bajo Tokens de Acceso Personales, describe para qué se usará el token y elige Crear Token. El token se muestra una sola vez: cópialo.
Con la API: POST /users/{user_id}/tokens con {"description": "backup script", "expires_at": 0} (expires_at en milisegundos; 0 para ninguno). El campo token de la respuesta es el secreto; su id es lo que se usa para revocarlo (POST /users/tokens/revoke con {"token_id": "…"}). Desde la línea de comandos: amctl token generate <user> <description>.
Un administrador del sistema puede limitar la vida útil de los tokens con Vida útil máxima de token de acceso personal (días) en la misma página de la Consola de sistema.
Tokens de sesión
Iniciar sesión con una contraseña devuelve un token de sesión en la cabecera de respuesta Token, el mismo tipo de sesión que usan las aplicaciones:
curl -i -X POST https://chat.example.com/api/v4/users/login \
-H 'Content-Type: application/json' \
-d '{"login_id": "maya", "password": "…"}'
Añade "token": "123456" para una cuenta con autenticación multifactor. Las sesiones expiran según la configuración de sesiones del servidor, y el punto de entrada de inicio de sesión tiene un límite de frecuencia (5 solicitudes por segundo). POST /users/logout termina la sesión.
X-CSRF-Token para cualquier cosa que no sea GET; un token en la cabecera Authorization no necesita cabecera CSRF.Aplicaciones OAuth 2.0
Para permitir que las personas inicien sesión en tu aplicación con su cuenta de Antimatter, registra una aplicación OAuth 2.0. Antimatter será entonces el proveedor OAuth:
- Habilitar el Proveedor de Servicios de OAuth 2.0 debe estar activado (Consola de sistema › Integraciones › Gestión de Integraciones,
ServiceSettings.EnableOAuthServiceProvider, activado por defecto). - Una persona con permiso para gestionar aplicaciones OAuth (administradores del sistema de forma predeterminada) abre Integraciones › Aplicaciones OAuth 2.0 (menú de la comunidad › Configuración del equipo › Integraciones) y añade la aplicación con sus URL de devolución de llamada. Obtiene un ID de cliente y un secreto de cliente.
- Tu aplicación envía a las personas a
https://chat.example.com/oauth/authorizeconresponse_type=code,client_id,redirect_uriystate. Se admite PKCE concode_challenge_method=S256. - Intercambia el código en
POST https://chat.example.com/oauth/access_token(grant_type=authorization_code, y luegorefresh_tokenpara renovarlo) y llama a la API conAuthorization: Bearer <access token>.
Las personas ven las aplicaciones que han autorizado en Perfil y configuración › Seguridad › Aplicaciones que has autorizado.
Recursos principales
| Qué | Puntos de conexión |
|---|---|
| Tú y otras personas | GET /users/me, GET /users/{user_id}, GET /users/username/{username}, POST /users/ids (una lista de IDs), POST /users/search, GET /users?in_team=…&in_channel=… |
| Comunidades | 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 |
| Canales | GET /users/me/teams/{team_id}/channels, GET /teams/name/{team}/channels/name/{channel}, POST /channels, POST /channels/direct (dos IDs de usuario), POST /channels/group, POST /channels/{channel_id}/members |
| Mensajes | 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 |
| Reacciones | POST /reactions con {"user_id", "post_id", "emoji_name"}, DELETE /users/{user_id}/posts/{post_id}/reactions/{emoji_name} |
| Archivos | POST /files (multipart: channel_id y files), y luego coloca los IDs devueltos en file_ids del mensaje; GET /files/{file_id} |
| Preferencias | GET /users/me/preferences, PUT /users/me/preferences |
| Integraciones | /hooks/incoming, /hooks/outgoing, /commands, /bots, /oauth/apps (consulta las páginas siguientes) |
| Servidor | GET /system/ping (estado de salud, no requiere token), GET /config/client?format=old (las configuraciones que ven los clientes) |
Publicar un mensaje
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"}'
Los mensajes usan Markdown. Añade "root_id" (el primer mensaje de un hilo) para responder en un hilo, "file_ids" para archivos y "props" para adjuntos y otros elementos adicionales (consulta Adjuntos).
Buscar el ID de un canal
curl -H "Authorization: Bearer $TOKEN" \
https://chat.example.com/api/v4/teams/name/lumen-labs/channels/name/ops-alerts
Los nombres son los que aparecen en el enlace del canal: /lumen-labs/channels/ops-alerts.
Paginación
Los puntos de conexión de listas aceptan page (desde 0) y per_page (60 por defecto, máximo 200). Sigue solicitando la siguiente página hasta que una página devuelva menos elementos que per_page.
Los mensajes de un canal (GET /channels/{channel_id}/posts) también aceptan since (un momento: todo lo que cambió desde entonces) o before y after (un ID de publicación). Se devuelven como una lista de publicaciones: {"order": [IDs, newest first], "posts": {ID: post}, "next_post_id", "prev_post_id"}.
Errores
Los errores usan los códigos de estado HTTP habituales (400 entrada incorrecta, 401 no autenticado, 403 no autorizado, 404 no encontrado, 501 función desactivada) y un cuerpo JSON:
{
"id": "api.context.permissions.app_error",
"message": "You do not have the appropriate permissions.",
"detailed_error": "",
"request_id": "jz8zm5nunpbt8f3gpqsz5dq6so",
"status_code": 403
}
Usa id en tu código: message se traduce al idioma del usuario. detailed_error está vacío a menos que el servidor se ejecute en modo desarrollador (ServiceSettings.EnableDeveloper). Cita request_id al administrador del servidor: aparece en los registros.
Límites de velocidad
El límite de velocidad está desactivado por defecto. Cuando un administrador del sistema lo activa (Consola de sistema › Entorno › Límite de Velocidad), cada cliente obtiene 10 solicitudes por segundo con ráfagas de 100 por defecto, contadas por dirección IP o por usuario. Las respuestas incluyen X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset; al superar el límite, el servidor responde 429 con una cabecera Retry-After en segundos. Algunos puntos de conexión, como el inicio de sesión, tienen su propio límite independientemente de la configuración.
Escribe scripts que esperen y reintente ante un 429, y evita las consultas repetidas: usa el WebSocket en su lugar.
El flujo de eventos WebSocket
Conéctate a wss://chat.example.com/api/v4/websocket. Autentícate mediante la cabecera Authorization: Bearer en la petición de conexión, o enviando esto como primer mensaje:
{"seq": 1, "action": "authentication_challenge", "data": {"token": "…"}}
El servidor envía un evento hello y, a continuación, un evento por todo lo que tengas permiso para ver:
{
"event": "posted",
"data": {"channel_id": "…", "channel_type": "O", "post": "{\"id\":\"…\",\"message\":\"Hello\",…}"},
"broadcast": {"channel_id": "…", "team_id": "", "user_id": "", "omit_users": null},
"seq": 4
}
Ten en cuenta que data.post es el mensaje como una cadena JSON. Eventos habituales: posted, post_edited, post_deleted, reaction_added, reaction_removed, typing, status_change, user_added, direct_added. Las aplicaciones envían sus propios eventos, nombrados custom_<app id>_<event>.
También puedes enviar acciones, cada una con un seq creciente: user_typing con {"channel_id", "parent_id"}, o get_statuses. Reconéctate cuando la conexión se interrumpa y recupera lo que hayas perdido con since (ver Paginación).
Antimatter añade estos eventos: agent_activity_changed y agent_post_stream (consulta Agentes), ai_tools_changed (consulta Agentes), community_rules_updated y community_rules_accepted (consulta Reglas de la comunidad), focus_ended (consulta Modo de concentración), community_attributes_updated, member_timeouts_updated, post_put_on_top, post_removed_from_top, server_categories_updated, channel_overrides_updated y category_overrides_updated. Un nuevo inicio de sesión en tu propia cuenta envía session_created a tus otras sesiones (session_id, create_at, platform, os, browser, mobile; nunca un token), y gestionar su advertencia envía session_revoked o session_acknowledged (session_id) a todas ellas.
Bibliotecas de cliente
- Ir:
model.Client4enserver/public/model(importargithub.com/mattermost/mattermost/server/public/model).model.NewAPIv4Client(url), luegoclient.SetToken(token);model.NewWebSocketClient4(wsURL, token)sigue los eventos. La copia de Antimatter añade métodos para sus propios puntos de conexión, comoUpdateMessageRequest,PutPostOnTopyExportChannel. - TypeScript: la clase
Client4de@mattermost/client, enwebapp/platform/client, con sus tipos enwebapp/platform/types. La copia en el repositorio de Antimatter incluye los puntos de conexión de Antimatter; el paquete publicado en npm por Mattermost no los incluye.
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)
}
Lo que Antimatter añade a la API
Estos puntos de conexión y campos no existen en un servidor de Mattermost. Todos ellos requieren una sesión.
| Punto de conexión | Qué hace |
|---|---|
GET /teams/{team_id}/user_attributes/fieldsPOST misma rutaPATCH, DELETE …/fields/{field_id} | Atributos de la comunidad: los campos de perfil que los administradores de una comunidad definen para sus miembros (hasta 20). Los miembros pueden leerlos; los administradores de la comunidad los crean y modifican. |
GET /teams/{team_id}/members/{user_id}/user_attributesPATCH misma rutaPOST /teams/{team_id}/user_attributes/values/search | Los valores de atributos de un miembro, como {field_id: value}. Los miembros establecen los propios (excepto los gestionados por administradores); los administradores establecen los de cualquiera. La búsqueda toma {"user_ids": [...]} (hasta 200). |
GET /teams/{team_id}/timeoutsPUT /teams/{team_id}/members/{user_id}/timeoutGET /teams/{team_id}/members/{user_id}/moderation | Tiempo de espera: {"until": time, "reason": "…"} impide que alguien publique, reaccione y suba archivos en la comunidad hasta ese momento (máximo 28 días); "until": 0 lo levanta. La vista de moderación indica cuándo se unieron y cuánto han publicado recientemente. |
GET /channels/{channel_id}/on_topPUT, DELETE /posts/{post_id}/on_top | Mensajes mantenidos en la parte superior de un canal (hasta 10). {"duration": ms}, como máximo 30 días; 0 los mantiene hasta que se eliminen. Cualquier persona que pueda leer el canal puede verlos. Para colocar un mensaje en la parte superior o retirarlo, se necesita el permiso para publicar en el canal y, en un canal de una comunidad, ser administrador del canal o de la comunidad, o tener Gestionar mensajes (delete_others_posts) allí. En un mensaje directo o de grupo, sus miembros pueden hacerlo. Se rechaza en canales archivados. |
GET, PUT /teams/{team_id}/rulesPOST /teams/{team_id}/rules/acceptGET /teams/{team_id}/rules/stats | Reglas de la comunidad: consulta Reglas de la comunidad. |
GET, POST, DELETE /users/me/focusGET /users/me/focus/summary?since=&until=POST /users/me/focus/summary/read | Modo de concentración: consulta Modo de concentración. |
POST /channels/{channel_id}/message_request | Actúa sobre tu propia solicitud de mensaje: {"action": "accept"}, "dismiss" o "request" para devolver una conversación entre las solicitudes. Devuelve tu miembro del canal. |
GET, PUT /channels/{channel_id}/overridesGET /channels/{channel_id}/overrides/effectiveGET, PUT /channels/{channel_id}/role_access | Lo que un rol o una persona puede hacer en un canal, y qué roles ven un canal restringido por rol. |
GET, PUT /teams/{team_id}/server_categoriesGET, PUT …/server_categories/overrides | Las categorías de canales compartidas por la comunidad, y las sobrescripciones que se aplican a todos los canales de una categoría. |
GET /groups/{group_id}/team_rolePUT …/team_role/permissions | Los permisos que otorga un rol de la comunidad. |
POST /teams/{team_id}/autolinks/preview | Prueba las reglas de autolinks de la comunidad en un texto. |
POST /channels/{channel_id}/export?format=csv&since=&until= | Descarga los mensajes de un canal como CSV (hasta 50.000), para las personas que pueden leer el canal. |
GET /posts/{post_id}/speech | El mensaje leído en voz alta, como audio, cuando se configura la conversión de texto a voz. |
POST /posts/{post_id}/move_to_channel | Mueve un hilo a otro canal de la misma comunidad: ver Mover un hilo. |
GET, POST /ai/connectionsGET, PATCH, DELETE /ai/connections/{connection_id}GET /ai/connections/usable | Conexiones de modelos, los proveedores de IA que utilizan las funciones de IA del servidor y las aplicaciones: ver Conexiones de modelos. |
GET, POST /ai/agentsGET, PATCH, DELETE /ai/agents/{agent_id}POST /ai/agents/{agent_id}/tasksGET /ai/agents/conversations | Agentes de IA, su actividad y las tareas y conversaciones del chat de IA: consulta Agentes. |
GET /ai/agents/{agent_id}/toolsGET /ai/agents/tools/cataloguePOST /ai/tools/calls/{call_id}/confirm, /declineGET, POST /ai/mcp_serversGET, PATCH, DELETE /ai/mcp_servers/{server_id}POST, DELETE /ai/mcp_servers/{server_id}/oauth | Las herramientas de un agente, el catálogo de herramientas que puede usar, confirmar o rechazar una llamada de herramienta pendiente y los servidores MCP disponibles para los agentes: consulta IA. |
GET /channels/{channel_id}/message_actionsPOST /posts/{post_id}/message_actions/{hook_id}GET, POST /hooks/outgoing/{hook_id}/icon | Acciones de mensajes: webhooks salientes que las personas ejecutan desde el menú del mensaje. Ver Acciones de mensajes. |
POST /users/me/sessions/acknowledge | Descarta la advertencia de nuevo inicio de sesión de una de tus propias sesiones: {"session_id"}. Solo la persona con sesión iniciada puede llamar a esto, para sus propias sesiones. |
PUT /channels/{channel_id}/type | Cambia el preajuste de un canal o su configuración: consulta Tipos de canal. |
GET /channels/{channel_id}/topicsPATCH /posts/{post_id}/topic | Los temas de un foro y aceptar una respuesta o establecer el estado de un tema: consulta Tipos de canal. |
Nuevos campos
- Comunidades (
TeamyPUT /teams/{team_id}/patch):all_channels_membership(todos están en cada canal que pueden ver),default_channel_id(a dónde van los recién llegados),welcome_channel_id(a dónde van los mensajes de unirse y salir, vacío si no hay ninguno),kind(communityoworkplace, mostrado en la interfaz como Guild y Espacio de trabajo: un gremio también activaall_channels_membership; las nuevas comunidades obtienen el valor deTeamSettings.DefaultCommunityKinddel servidor,community/Gremio por defecto),member_list_view(cómo agrupa la lista de miembros:choice, el predeterminado, permite que cada miembro elija;rolesoteamslo fijan),hide_role_colors,require_team_badges,name_display(cómo muestra la comunidad a las personas: vacío para el valor predeterminado del servidor),onboardingyautolinks(ambas cadenas JSON). - El canal predeterminado de una nueva comunidad es
general(General), notown-square. La actualización a la versión 0.9.0 cambia el nombre detown-squareageneralen cada comunidad, conservando su ID, a menos que la comunidad ya tenga un canalgeneral. Los nombres antiguos siguen funcionando:GET /teams/{team_id}/channels/name/town-square,GET /teams/name/{team}/channels/name/town-squarey el campochannelde un webhook entrante devuelven el canal predeterminado de la comunidad cuando no existe un canal con ese nombre. Utilicedefault_channel_ido el nombregeneralen el nuevo código. - Enlaces de canal:
#channel-nameenlaza a un canal, al igual que~channel-name. La propiedadchannel_mentionsde la publicación lista los canales enlazados de cualquiera de las dos formas; un#wordque no corresponde a ningún canal se mantiene como etiqueta. La interfaz web y la aplicación móvil de Antimatter escriben#name. - Los Canales Archivados siguen admitiendo nuevos miembros (
POST /channels/{channel_id}/members), que pueden leer su historial. Nadie puede publicar, reaccionar, fijar, poner un mensaje en la parte superior, cambiar el nombre del canal ni modificar los roles de sus miembros allí. - Respuestas en línea: una publicación con
"props": {"reply_to": "<post id>"}cita ese mensaje del mismo canal. Añade"reply_to_mention": falsepara no notificar a su autor. El servidor rellenametadata.reply_tocon una descripción breve del mensaje citado. - Alertas de contenido:
"props": {"spoiler": true}oculta el texto y los archivos de una publicación hasta que el lector elige verlos. - Ignorar y bloquear son preferencias: categoría
user_block, nombrada con el ID de la otra persona, con el valorignoreoblock. Si un mensaje directo espera como solicitud está ennotify_props.dm_requestde tu miembro de canal (pending,acceptedodismissed). - Adjuntos de mensajes pueden llevar
stagesy unchart: ver Tarjetas. - Canales (
Channel): lospropsde cada canal de la comunidad siempre llevanam_type(text,voiceoforum),am_mode,am_root_posts(membersomoderators) yam_preset(el ID del preajuste correspondiente, o vacío para configuraciones personalizadas), y, para los foros,am_forum_view(topicsoimageboard). Una carga útil sinam_typesignifica que no se completó, nunca que el canal sea de texto. Los mensajes directos y de grupo no llevan ninguno de estos campos. Consulta Tipos de canal.
Tipos de canal
Un canal de la comunidad es text, voice o forum (su tipo, fijo cuando se crea), en uno de los modos de su tipo, con configuraciones encima. Un preajuste es un conjunto con nombre de un modo y configuraciones que un cliente ofrece como una sola opción; los preajustes no se almacenan, por lo que el preajuste de un canal (am_preset) se deriva de sus configuraciones, y muestra "" cuando no coinciden con ningún preajuste ("Personalizado").
| ID del preajuste | Tipo | Modo | root_posts | view |
|---|---|---|---|---|
text_chat | text | chat | members | – |
text_announcement | text | chat | moderators | – |
voice_talk | voice | talk | members | – |
voice_stage | voice | stage | members | – |
forum_agora | forum | discussion | members | topics |
forum_qa | forum | qa | members | topics |
forum_ideas | forum | ideas | members | topics |
forum_announcement | forum | discussion | moderators | topics |
forum_imageboard | forum | discussion | members | imageboard |
Crear y cambiar el tipo de un canal
POST /channels lee props.am_preset (primero se expande) y, encima, los valores individuales de props.am_type, am_mode, am_root_posts y am_forum_view; cualquier otra propiedad am_ se descarta. No se permite crear un canal de voz si la aplicación de canales de voz no está activa (400 api.channel.type.voice_unavailable.app_error); tampoco se permite crear un mensaje directo o de grupo con propiedades de tipo (api.channel.type.direct.app_error).
curl -X POST https://chat.example.com/api/v4/channels \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"team_id": "'$TEAM_ID'", "name": "help-desk", "display_name": "Help desk", "type": "O",
"props": {"am_preset": "forum_qa"}}'
PUT /channels/{channel_id}/type cambia el preajuste o la configuración de un canal existente: {"preset"?, "mode"?, "root_posts"?, "view"?}, con el preajuste expandido primero, igual que en la creación. Requiere manage_public_channel_properties o manage_private_channel_properties en el canal (evento de auditoría updateChannelType) y responde con el canal completo. Se rechaza un cuerpo que indique otro type (api.channel.type.fixed.app_error): el tipo en sí nunca cambia. Otros errores: api.channel.type.invalid.app_error (un valor desconocido, un modo que no corresponde al tipo del canal o una vista en un canal que no es foro).
La importación y exportación masiva (amctl import bulk) incluyen los mismos cuatro campos en la línea de un canal: am_type, am_mode, am_root_posts, am_forum_view. Una línea de canal sin estos campos se importa como un canal de Chat de texto normal, como antes de los tipos de canal.
Quién inicia conversaciones o abre temas
Publicar un mensaje raíz (a diferencia de una respuesta) requiere el permiso create_root_post, que aparece en el editor de anulaciones como Iniciar conversaciones (texto, voz) o Abrir temas (foros). Ningún rol lo tiene por defecto: antes de las anulaciones, lo tienen todas las personas que pueden publicar cuando root_posts es members, y solo los moderadores del canal (administradores del sistema, administradores de la comunidad, administradores del canal y cualquier persona con Gestionar mensajes allí) cuando es moderators. Las anulaciones de rol o de persona sobre create_root_post se aplican encima, como con cualquier permiso. Una respuesta (con root_id establecido) solo requiere el derecho ordinario a publicar.
Una publicación raíz que no cumpla esto devuelve 403: app.post.root_posts.moderators_only.app_error cuando root_posts es moderators y quien publica no es moderador del canal, o app.post.root_posts.denied.app_error por una denegación por anulación. Esta comprobación se aplica a la API REST, los bots, los webhooks entrantes y salientes (en el creador del webhook) y las respuestas de comandos con barra diagonal; no se aplica a las publicaciones de aplicaciones (la API de complementos) ni a las copias de un hilo movido (en ese caso se comprueba a la persona que mueve el hilo). La importación masiva también la omite.
Los foros solo aceptan temas
En un foro, una publicación raíz necesita props.am_thread_title (el título de su tema), sin espacios en blanco al inicio o final, de 1 a 100 caracteres, de cualquier origen, incluidas las aplicaciones y los hilos movidos: si falta, devuelve 400 app.post.forum.title_required.app_error; si es demasiado largo, app.post.forum.title_too_long.app_error. Las respuestas no necesitan título, y cualquier título enviado en una respuesta se descarta. Editar la raíz de un tema sin título, o con uno vacío, mantiene su título existente; uno no vacío lo renombra. Mover un hilo a un foro (más abajo) también requiere un título, ya sea de la solicitud o del propio hilo.
La respuesta aceptada de un tema de preguntas y respuestas (am_accepted_answer, el ID de una respuesta) y el estado de un tema de ideas (am_topic_status, junto con am_topic_status_by y am_topic_status_at) solo se escriben mediante PATCH /posts/{post_id}/topic; cualquiera de estos tres campos enviados en la creación, actualización o parche, por un cliente o una aplicación, se descarta y se conserva el valor almacenado.
Listar los temas de un foro
GET /channels/{channel_id}/topics?sort=votes&per_page=25
Requiere el permiso para leer el canal. Parámetros de consulta:
sort:activity(predeterminado),newest,votes,unanswered(solo preguntas y respuestas) ostatus(solo ideas).answered(true/false, preguntas y respuestas) ystatus(uno deopen,planned,in_progress,done,declined, ideas): filtrar por estos. Usar cualquiera fuera de su modo devuelve400.mine=true: solo los temas que iniciaste.following=true: solo los que sigues.q: coincide con el título o el texto de la primera publicación, hasta 200 caracteres.before: elnext_cursorde la página anterior.per_page: 25 por defecto, 100 como máximo.replies: de 0 a 5 de las últimas respuestas de cada tema, para una vista de foro de imágenes.
Respuesta: {"topics": [{"post", "reply_count", "last_reply_at", "participant_ids", "votes", "accepted_answer_id", "status", "on_top", "replies": [post, …]}], "next_cursor"}. Los votos son las reacciones 👍 (+1 y thumbsup se cuentan juntas), menos el autor del propio tema; accepted_answer_id solo se incluye en foros de preguntas y respuestas, status solo en foros de ideas. Los temas fijados en la parte superior del canal aparecen primero.
Aceptar una respuesta o establecer el estado de un tema
curl -X PATCH https://chat.example.com/api/v4/posts/$POST_ID/topic \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"accepted_answer_id": "'$REPLY_ID'"}'
post_id debe ser la publicación raíz de un tema de foro; de lo contrario, 400 app.topic.not_a_topic.app_error. accepted_answer_id (el ID de una respuesta, o "" para revocarlo) requiere un foro de preguntas y respuestas; status requiere un foro de ideas; usar cualquiera de ellos en el modo incorrecto genera 400 app.topic.mode.app_error.
- Aceptar una respuesta: solo el autor de la pregunta del tema (mientras pueda seguir publicando allí) o un moderador del canal; de lo contrario,
403 app.topic.accept.not_allowed.app_error. La respuesta debe ser una respuesta activa y no del sistema de ese tema; de lo contrario,400 app.topic.accept.not_a_reply.app_error. Eliminar la respuesta aceptada borra la respuesta. - Establecer un estado: solo moderadores del canal; de lo contrario,
403 app.topic.status.not_allowed.app_error; un valor desconocido genera400 app.topic.status.invalid.app_error.
Cada cambio publica una respuesta del sistema en el tema (informando a sus seguidores) y responde con la publicación raíz del tema, tal como está ahora. En un foro de ideas, votar por un tema también hace que lo sigas.
Mover un hilo
POST /posts/{post_id}/move_to_channel mueve un hilo, su primer mensaje y todas sus respuestas, a otro canal de la misma comunidad. post_id puede ser el primer mensaje o cualquier respuesta.
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(obligatorio): el canal al que se mueve el hilo.flatten(por defectofalse):trueconvierte las respuestas en mensajes del canal de destino en lugar de un hilo. La opción Convertir en canal de Fusion lo utiliza. Se rechaza cuando el destino es un foro (400 app.post.move_thread.forum_flatten.app_error): sus respuestas se convertirían en temas sin título.title: el título del tema, cuando el destino es un foro. Sin él, se mantiene el título del propio hilo, si lo tiene; la ausencia de ambos genera400 app.post.move_thread.forum_title_required.app_error. Se ignora si el destino no es un foro.
Los mensajes conservan sus autores, horas, reacciones y archivos. Sus copias llevan la propiedad silent_notification, por lo que no notifican a nadie. Los mensajes originales se eliminan. La respuesta es el primer mensaje movido, con su nuevo ID.
Quién puede mover un hilo
- Debe poder leer el hilo y publicar en el canal de destino.
- Los administradores del canal, los administradores de la comunidad, las personas con Gestionar mensajes (
delete_others_posts) en el canal y los administradores del sistema pueden mover cualquier hilo. - El autor del hilo puede moverlo cuando ambos canales son públicos, o cuando todos los mensajes del hilo son suyos. En caso contrario, la respuesta es
403con el IDapi.post.move_thread.private_needs_moderator.app_error. - Ambos canales deben pertenecer a la misma comunidad (no se permiten mensajes directos ni de grupo), ser diferentes y no estar archivados. No se pueden mover mensajes del sistema ni mensajes que se eliminan tras la lectura.
- Se rechaza un hilo con más de
WranglerSettings.MoveThreadMaxCountmensajes (100 por defecto; 0 para sin límite).
El puntero y la nota
La operación de mover deja dos mensajes del sistema de tipo system_wrangler: un puntero en el canal de origen ("Un hilo con 13 mensajes se ha movido a #target") y una nota en el destino ("Este hilo se movió desde #source"), bajo el hilo movido o, con flatten, después de sus mensajes. Además del texto del mensaje, sus propiedades permiten a un cliente dibujar el nombre del canal como un enlace:
- El puntero:
MovedThreadPermalink,NumMessages,MovedThreadChannelId,MovedThreadTeamName,MovedThreadPostIdy, para un destino público,MovedThreadChannelNameyMovedThreadChannelDisplayName. - La nota:
MovedFromChannelId,MovedFromTeamName,MovedFromPostId(el puntero),MovedFromPermalinky, para un origen público,MovedFromChannelNameyMovedFromChannelDisplayName.
Ambos también tienen un TranslationID. El nombre de un canal privado se omite: los lectores ven "otro canal". El propio POST /posts/{post_id}/move de Mattermost permanece desactivado en Antimatter; usa este punto de conexión.
Reglas de la comunidad
Los administradores de una comunidad pueden exigir a sus miembros que acepten sus normas antes de publicar en sus canales. Mientras las normas estén activas, un miembro que no haya aceptado la versión actual no podrá publicar, responder, enviar archivos, programar, editar o restaurar un mensaje, ni ejecutar un comando que publique en los canales de la comunidad: el servidor responde 403 con el ID api.post.community_rules_not_accepted, y un mensaje programado que llegue a su hora en ese momento fallará con el código de error community_rules_not_accepted. La lectura, las reacciones y los mensajes directos y de grupo no se ven afectados; tampoco los bots, los webhooks entrantes, los mensajes del sistema ni los administradores de la comunidad (manage_team, permiso que también tienen los administradores del sistema).
| Punto de conexión | Qué hace |
|---|---|
GET /teams/{team_id}/rules | Las normas (enabled, text en Markdown, version), junto con la última versión que aceptaste (accepted_version) y si debes aceptarlas antes de publicar (must_accept). Para los miembros y para cualquier persona que pueda unirse a una comunidad abierta. |
PUT /teams/{team_id}/rules | Guarda las normas con {"enabled", "text", "ask_again"} (manage_team). ask_again crea una nueva versión que todos deben aceptar de nuevo; el primer guardado corresponde a la versión 1. El texto es obligatorio para activarlas y debe tener como máximo 16 000 caracteres. |
POST /teams/{team_id}/rules/accept | Acepta, como miembro, la version que has leído; devuelve 409 si se ha guardado una versión más nueva desde entonces, y 0 si aceptas la actual. |
GET /teams/{team_id}/rules/stats | El número de miembros de la comunidad que han aceptado la versión actual (manage_team). |
El evento community_rules_updated se envía a la comunidad cuando un administrador guarda las normas (team_id, enabled, version), y community_rules_accepted se envía a quien las aceptó, en todas sus sesiones (team_id, version).
Modo de concentración
El modo de concentración es un «No molestar» con hora de finalización: las notificaciones se silencian (sin notificaciones push, por correo electrónico, de escritorio ni persistentes, incluidos los mensajes urgentes), el motivo se convierte en el estado personalizado con el emoji 🎯 (dart), que caduca al finalizar, y las aplicaciones de Antimatter ocultan las insignias de no leídos de la propia persona. Solo la persona interesada puede utilizar estos endpoints (me o su propio ID): nadie más, incluidos los administradores del sistema, puede iniciar, finalizar ni leer el modo de concentración de otra persona ni su resumen.
| Punto de conexión | Qué hace |
|---|---|
GET /users/me/focus | El modo de concentración actual (active, start_at, end_at, reason), o el último una vez finalizado (ended_at establecido), o todos con valor cero. |
POST /users/me/focus | Inicia el enfoque o, mientras está enfocado, cambia el final y el motivo: {"duration": "1h30m", "reason": "…"}, o {"end_at": ms} en lugar de la duración, desde un minuto hasta una semana después. "duration": "tomorrow" termina a las 09:00 del día siguiente en la zona horaria de la persona. La hora de finalización se redondea hacia abajo al minuto; el motivo tiene un máximo de 100 caracteres. Requiere que los estados de usuario estén activados (ServiceSettings.EnableUserStatuses); los bots no pueden enfocarse. |
DELETE /users/me/focus | Finaliza el enfoque ahora. |
GET /users/me/focus/summary?since=&until= | Lo que se perdió en esa ventana (el start_at y el ended_at de un enfoque): los canales con mensajes sin leer de otras personas (mensajes directos y de grupo primero, luego aquellos con menciones; canales silenciados solo con una mención) y, con hilos de respuestas colapsados, los hilos seguidos con nuevas respuestas, cada uno con su recuento, menciones y último remitente. |
POST /users/me/focus/summary/read | Con {"since", "until"}: marca esos canales e hilos como leídos hasta until únicamente; lo posterior permanece sin leer. |
Mientras alguien está enfocado, su usuario tiene la propiedad focus_until (milisegundos), que los clientes utilizan para mostrar el estado de enfoque; no se puede cambiar a través del perfil. El enfoque termina cuando la persona lo finaliza, cuando llega su hora (en un minuto) o cuando selecciona un estado manualmente, el cual permanece entonces. De lo contrario, se restauran el estado y el estado personalizado anteriores. Todas sus sesiones reciben entonces el evento focus_ended, con start_at, end_at y ended_by (user, timer o status).
Conexiones de modelos
Una conexión de modelo es la dirección y la clave de un proveedor de IA, configurada por un administrador. Las funciones de IA del servidor y las aplicaciones que llaman al servicio de IA las utilizan. Los administradores del sistema gestionan las conexiones de todo el servidor (manage_ai_connections); los administradores de una comunidad gestionan las de su propia comunidad (manage_team_ai_connections). Las claves son solo de escritura: ninguna respuesta las incluye, solo api_key_set y api_key_last4.
| Punto de conexión | Qué hace |
|---|---|
GET /ai/connections?scope= | Las conexiones que gestionas. scope es server o el ID de una comunidad; sin él, todos los ámbitos que gestionas. |
POST /ai/connections | Añade una conexión (responde 201). |
POST /ai/connections/test, POST /ai/connections/models | Prueba una conexión o lista sus modelos antes de guardarla. Mismo cuerpo que al añadirla; no se guarda nada. |
GET, PATCH (o PUT), DELETE /ai/connections/{connection_id} | Lee, modifica o elimina una conexión. Un parche solo incluye los campos a cambiar; omite api_key para conservar la clave. |
POST /ai/connections/{connection_id}/test | Prueba una conexión guardada: {"ok", "error", "status_code", "latency_ms", "model_count"}. |
GET /ai/connections/{connection_id}/models | Los modelos que ofrece el proveedor. |
GET /ai/connections/{connection_id}/usage | Las solicitudes y los tokens de este mes. |
GET /ai/connections/usable?channel_id=&team_id= | Para cualquier persona iniciada en la sesión: las conexiones que puede usar allí, con sus modelos y capacidades, nunca sus claves o direcciones. |
El cuerpo de una nueva conexión:
{
"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 es openai_compatible o anthropic. scope tiene como valor predeterminado server; el ID de una comunidad la convierte en la conexión de esa comunidad. En limits, 0 significa sin límite; las solicitudes por minuto se cuentan en cada servidor, y los presupuestos de tokens por mes calendario (UTC) para todo el clúster. sharing indica qué comunidades pueden usar una conexión de todo el servidor (los mensajes directos y de grupo requieren all_teams), y audience quién puede usarla; solo invitados con allow_guests. Los cambios se escriben en el registro de auditoría. La guía de administración explica la configuración: ver IA.
Agentes
Un agente es una cuenta de bot del tipo agent (is_agent en su usuario), con su configuración: su ámbito (server o el ID de una comunidad), integrado o external, y para un agente integrado, su conexión de modelo, modelo, prompt del sistema y razonamiento. Los administradores del sistema gestionan los agentes de todo el servidor (manage_system), los administradores de una comunidad gestionan los de su comunidad (manage_team); además, manage_own_agent para un agente que hayan creado y manage_others_agent para los demás. El ID del agente en estas rutas es su ID de usuario. La ejecución de un agente externo y cómo se establece el estado de un agente integrado se explican en Bots; PUT /users/{user_id}/status y los endpoints de estado personalizado rechazan el ID de un agente integrado, incluso para los administradores del sistema (403, api.status.agent.app_error).
| Punto de conexión | Qué hace |
|---|---|
GET /ai/agents?scope= | Los agentes que gestionas: scope es server o el ID de una comunidad; si se omite, se incluyen todos los agentes que gestionas. |
POST /ai/agents | Crea un agente y su cuenta (devuelve 201). El agente de una comunidad se une a dicha comunidad. |
GET, PATCH, DELETE /ai/agents/{agent_id}POST …/enable, POST …/disable | Lee, modifica, elimina (su cuenta se desactiva, pero sus mensajes permanecen) y activa o desactiva un agente. |
GET /ai/agents/connections?scope=GET /ai/agents/connections/{connection_id}/models | Las conexiones de modelos que un agente de ese ámbito puede utilizar, y sus modelos. |
GET, PUT /ai/agents/{agent_id}/activityPOST /ai/agents/activity/ids | Lo que está haciendo un agente; lo establecen su propio token o sus gestores. El lote admite hasta 200 IDs de usuario. |
POST /ai/agents/{agent_id}/tasks | Solicita a un agente integrado que puedes usar para una tarea: summarize_thread, find_action_items o find_open_questions (en el post_id de un hilo, o en un channel_id desde since en adelante), summarize_channel, ask_channel (con una question) o summarize_transcription (la publicación de la transcripción de una llamada). La solicitud se convierte en tu mensaje en tu mensaje directo con el agente, en tu idioma, y se responde allí; la respuesta (201) es ese mensaje. Una publicación o canal que no puedas leer se trata como si no existiera. |
GET /ai/agents/conversations?agent_id=&before=&per_page= | Tus conversaciones con los agentes que puedes usar, ordenadas de la más reciente a la más antigua: los hilos que iniciaste en tus mensajes directos con ellos, y los hilos en otros lugares en los que uno de ellos respondió y que sigues. before (una hora) muestra la siguiente página; per_page es como máximo 100. |
El cuerpo de un nuevo agente:
{
"username": "researcher",
"display_name": "Researcher",
"description": "Answers with sources",
"scope": "server",
"external": false,
"connection_id": "…",
"model": "",
"system_prompt": "You research questions and answer with sources.",
"reasoning": {"enabled": false, "effort": "", "budget_tokens": 0},
"max_tool_turns": 0,
"access": {"everyone": true, "team_ids": [], "channel_ids": [], "roles": [], "user_ids": [], "allow_guests": false}
}
Un model vacío utiliza el modelo de chat de la conexión. access.channel_ids hace referencia a los miembros de esos canales. Un agente externo no tiene conexión, modelo ni prompt de sistema. GET /agents enumera los agentes con los que puedes hablar, tal como los lee la aplicación de chat de IA.