REST API

Antimatter 应用的所有操作都通过同一个 HTTP API 进行,因此您的脚本也可以这样做。本页介绍如何进行身份验证、您将最常使用的资源、响应和错误的格式,以及如何通过 WebSocket 实时跟踪事件。

基础

  • 基础 URL:https://chat.example.com/api/v4。以下所有端点均相对于此 URL。
  • 格式:输入和输出均为 JSON,文件上传除外(使用 multipart)。请发送 Content-Type: application/json。
  • ID:每个对象都有一个 26 个字符的 id。时间表示自 Unix 纪元以来的毫秒数(create_at、update_at、delete_at;0 表示“未删除”)。
  • 社区即团队:API 将其称为 teams,并带有 team_id。
  • 您自己:在任何接受 user_id 的路径中,me 表示当前登录的用户,例如 GET /users/me。

完整的端点列表位于服务器代码中:路由声明在 server/channels/api4/*.go 中,类型定义在 server/public/model 中。仓库 api/v4/source 文件夹中的 OpenAPI 描述涵盖了从 Mattermost 继承的端点,以及 Antimatter 的一些新增功能:移动话题、模型连接和消息操作。其余部分仅在代码中提供。

身份验证

除登录和少数公开端点外,每个请求都需要在 Authorization 头中包含令牌:

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

此处接受三种类型的令牌:个人访问令牌、会话令牌和 OAuth 访问令牌。也接受 Authorization: Token …。查询字符串中的令牌(?access_token=)仅对 OAuth 访问令牌有效。

个人访问令牌(推荐用于脚本)

个人访问令牌代表您进行操作,除非您设置了过期日期,否则不会过期,并且可以随时撤销。个人访问令牌默认处于关闭状态:

  1. 系统管理员需在 系统控制台 › 集成 › 集成管理 中开启 开启个人访问令牌(ServiceSettings.EnableUserAccessTokens、AM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS)。
  2. 对于每位可能创建令牌的用户,系统管理员需打开 系统控制台 › 用户管理 › 用户,在该用户的菜单中选择 管理角色,并勾选 允许此账户生成个人访问令牌。系统管理员始终可以创建令牌。
  3. 该用户点击屏幕右上角的头像,然后进入 个人资料与设置 › 安全。在 个人访问令牌 部分,描述该令牌的用途,然后选择 创建新令牌。令牌仅显示一次:请复制它。

通过 API:POST /users/{user_id}/tokens,请求体为 {"description": "backup script", "expires_at": 0}(expires_at 以毫秒为单位;0 表示永不过期)。响应中的 token 字段即为密钥;其 id 用于撤销该令牌(POST /users/tokens/revoke,请求体为 {"token_id": "…"})。从命令行:amctl token generate <user> <description>。

系统管理员可以在同一系统控制台页面上,通过 个人访问令牌最长生命周期(天) 设置令牌的最长有效期。

会话令牌

使用密码登录会在 Token 响应头中返回一个会话令牌,这与应用程序使用的会话类型相同:

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

对于启用了多因素认证的账户,请添加 "token": "123456"。会话的过期时间遵循服务器的会话设置,且登录端点受到速率限制(每秒 5 次请求)。POST /users/logout 用于结束会话。

浏览器使用 Cookie 进行身份验证。对于除 GET 以外的任何请求,通过 Cookie 进行身份验证的请求还必须发送 X-CSRF-Token 头;而 Authorization 头中的令牌则无需 CSRF 头。

OAuth 2.0 应用

若要允许用户使用其 Antimatter 账户登录您的应用程序,请注册一个 OAuth 2.0 应用。此时 Antimatter 充当 OAuth 服务提供商:

  1. 必须开启 启动 OAuth 2.0 服务提供商(位于 系统控制台 › 集成 › 集成管理,配置项为 ServiceSettings.EnableOAuthServiceProvider,默认开启)。
  2. 拥有管理 OAuth 应用权限的人员(默认为系统管理员)点击自己的头像,然后进入 集成 › OAuth 2.0 应用,添加该应用及其回调 URL。他们将获得一个客户端 ID 和一个客户端密钥。
  3. 您的应用将用户重定向至 https://chat.example.com/oauth/authorize,并附带 response_type=code、client_id、redirect_uri 和 state。支持使用 code_challenge_method=S256 的 PKCE。
  4. 随后,应用通过 POST https://chat.example.com/oauth/access_token 交换授权码(grant_type=authorization_code,之后使用 refresh_token 进行续期),并在调用 API 时携带 Authorization: Bearer <access token>。

用户可以在 个人资料与设置 › 安全 › 您已授权的应用 中查看已授权的应用。

主要资源

内容端点
您和其他用户GET /users/me、GET /users/{user_id}、GET /users/username/{username}、POST /users/ids(ID 列表)、POST /users/search、GET /users?in_team=…&in_channel=…
团队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
频道GET /users/me/teams/{team_id}/channels、GET /teams/name/{team}/channels/name/{channel}、POST /channels、POST /channels/direct(两个用户 ID)、POST /channels/group、POST /channels/{channel_id}/members
消息(帖子)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
表情回应POST /reactions,参数为 {"user_id", "post_id", "emoji_name"};DELETE /users/{user_id}/posts/{post_id}/reactions/{emoji_name}
文件POST /files(multipart 格式:包含 channel_id 和 files),然后将返回的 ID 放入帖子的 file_ids 字段;GET /files/{file_id}
偏好设置GET /users/me/preferences, PUT /users/me/preferences
集成/hooks/incoming、/hooks/outgoing、/commands、/bots、/oauth/apps(详见后续页面)
服务器GET /system/ping(健康检查,无需令牌)、GET /config/client?format=old(客户端可见的设置)

发布消息

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

消息使用 Markdown 格式。添加 "root_id"(话题的首条帖子)可在话题中回复;添加 "file_ids" 用于文件;添加 "props" 用于附件及其他附加信息(参见 附件)。

查找频道 ID

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

名称即频道链接中的名称:/lumen-labs/channels/ops-alerts。

分页

列表端点接受 page(从 0 开始)和 per_page(默认 60,最大 200)。持续请求下一页,直到返回的页面长度小于 per_page 为止。

频道的消息(GET /channels/{channel_id}/posts)也接受 since(时间:返回自该时间点以来发生的所有变更),或 before 和 after(帖子 ID)。返回结果为帖子列表:{"order": [IDs, newest first], "posts": {ID: post}, "next_post_id", "prev_post_id"}。

错误

错误使用标准的 HTTP 状态码(400 输入错误,401 未登录,403 权限不足,404 未找到,501 功能已关闭)以及 JSON 响应体:

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

在代码中使用 id:message 会翻译为用户的语言。detailed_error 仅在服务器运行于开发者模式(ServiceSettings.EnableDeveloper)时非空。向服务器管理员引用 request_id:它记录在日志中。

速率限制

速率限制默认关闭。当系统管理员启用它(系统控制台 › 环境 › 速率限制)时,每个客户端默认每秒 10 次请求,突发上限为 100 次,按 IP 地址或用户计数。响应包含 X-RateLimit-Limit、X-RateLimit-Remaining 和 X-RateLimit-Reset;超出限制时,服务器返回 429 并附带以秒为单位的 Retry-After 头。某些端点(如登录)无论设置如何都有独立的限制。

编写脚本,在遇到 429 时等待并重试,并避免轮询:请改用 WebSocket。

WebSocket 事件流

连接到 wss://chat.example.com/api/v4/websocket。认证方式有两种:在连接请求中使用 Authorization: Bearer 头,或发送以下消息作为第一条消息:

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

服务器发送 hello 事件,随后发送您有权查看的所有事件:

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

注意 data.post 是作为 JSON 字符串的帖子。常见事件包括:posted、post_edited、post_deleted、reaction_added、reaction_removed、typing、status_change、user_added、direct_added。应用发送其自身的事件,命名为 custom_<app id>_<event>。

您还可以发送操作,每个操作带有递增的 seq:user_typing 附带 {"channel_id", "parent_id"},或 get_statuses。连接断开时重新连接,并使用 since 获取遗漏的内容(参见 分页)。

Antimatter 添加了以下事件:community_attributes_updated、member_timeouts_updated、post_put_on_top、post_removed_from_top、server_categories_updated、channel_overrides_updated 和 category_overrides_updated。

客户端库

  • Go:位于 server/public/model 中的 model.Client4(导入 github.com/mattermost/mattermost/server/public/model)。使用 model.NewAPIv4Client(url),然后调用 client.SetToken(token);model.NewWebSocketClient4(wsURL, token) 用于跟踪事件。Antimatter 的副本为其自身的端点添加了方法,例如 UpdateMessageRequest、PutPostOnTop 和 ExportChannel。
  • TypeScript:@mattermost/client 中的 Client4 类,位于 webapp/platform/client,其类型定义位于 webapp/platform/types。Antimatter 仓库中的副本包含 Antimatter 的端点;而 Mattermost 在 npm 上发布的包则不包含。
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)
}

Antimatter 对 API 的扩展

这些端点和字段在 Mattermost 服务器上不存在。它们都需要会话。

端点功能说明
GET /teams/{team_id}/user_attributes/fields
POST 相同路径
PATCH、DELETE …/fields/{field_id}
社区属性:社区管理员为其成员定义的个人资料字段(最多 20 个)。成员可以读取这些字段;社区管理员负责创建和修改它们。
GET /teams/{team_id}/members/{user_id}/user_attributes
PATCH 相同路径
POST /teams/{team_id}/user_attributes/values/search
成员的属性值,格式为 {field_id: value}。成员可以设置自己的值(管理员管理的字段除外);管理员可以设置任何人的值。搜索接受 {"user_ids": [...]}(最多 200 个)。
GET /teams/{team_id}/timeouts
PUT /teams/{team_id}/members/{user_id}/timeout
GET /teams/{team_id}/members/{user_id}/moderation
超时设置:{"until": time, "reason": "…"} 会在指定时间之前(最长 28 天)阻止该用户在社区中发帖、发送表情回应或上传内容;"until": 0 则会解除限制。管理视图会显示该用户的加入时间以及近期的发帖量。
GET /channels/{channel_id}/on_top
PUT, DELETE /posts/{post_id}/on_top
将消息固定在频道顶部(最多 10 条)。{"duration": ms},最长 30 天;设为 0 则一直保留直到移除。任何能读取该频道的人都可以查看列表。将消息置顶或取消置顶需要拥有在该频道发帖的权限,并且,在社区频道中,还需是频道或社区的管理员,或拥有 管理消息(delete_others_posts)权限。在私信或群组消息中,其成员可以执行此操作。已归档的频道中不允许此操作。
POST /channels/{channel_id}/message_request处理您自己的消息请求:{"action": "accept"}、"dismiss" 或 "request"(将对话重新放回请求列表)。返回您的频道成员信息。
GET, PUT /channels/{channel_id}/overrides
GET /channels/{channel_id}/overrides/effective
GET, PUT /channels/{channel_id}/role_access
角色或个人在频道中可以执行的操作,以及哪些角色可以看到受角色限制的频道。
GET, PUT /teams/{team_id}/server_categories
GET, PUT …/server_categories/overrides
社区共享的频道分类,以及适用于该分类中所有频道的覆盖设置。
GET /groups/{group_id}/team_role
PUT …/team_role/permissions
社区角色授予的权限。
POST /teams/{team_id}/autolinks/preview使用社区的自动链接规则处理文本。
POST /channels/{channel_id}/export?format=csv&since=&until=将频道的消息下载为 CSV 文件(最多 50,000 条),适用于能读取该频道的人员。
GET /posts/{post_id}/speech当配置了文本转语音时,以音频形式朗读的消息。
POST /posts/{post_id}/move_to_channel将话题移动到同一社区的另一个频道:参见 移动话题。
GET, POST /ai/connections
GET, PATCH, DELETE /ai/connections/{connection_id}
GET /ai/connections/usable
模型连接,即服务器 AI 功能和应用程序使用的 AI 提供商:参见 模型连接。
GET /channels/{channel_id}/message_actions
POST /posts/{post_id}/message_actions/{hook_id}
GET, POST /hooks/outgoing/{hook_id}/icon
消息操作:用户从消息菜单触发的出站 Webhook。参见 消息操作。

新字段

  • 社区(Team,以及 PUT /teams/{team_id}/patch):all_channels_membership(所有成员都加入他们可见的所有频道)、default_channel_id(新成员加入的频道;为空表示 General)、kind(community 或 workplace:在工作区中,频道管理员也可以归档他们的频道;新社区使用服务器的 TeamSettings.DefaultCommunityKind,默认为 community)、member_list_view(成员列表如何分组:choice(默认值)允许每位成员自行选择;roles 或 teams 则固定分组方式)、hide_role_colors、require_team_badges、onboarding 和 autolinks(均为 JSON 字符串)。
  • 新社区的默认频道是 general(General),而不是 town-square。0.9.0 升级会将每个社区的 town-square 重命名为 general,并保留其 ID,除非该社区已经存在 general 频道。旧名称仍然有效:当没有频道使用这些名称时,GET /teams/{team_id}/channels/name/town-square、GET /teams/name/{team}/channels/name/town-square 以及传入 Webhook 的 channel 都会返回该社区的默认频道。在新代码中,请使用 default_channel_id 或名称 general。
  • 频道链接:#channel-name 用于链接频道,~channel-name 同样有效。帖子的 channel_mentions 属性会列出通过这两种方式链接的频道;如果 #word 不对应任何频道,则它保持为标签。Antimatter 的 Web 界面和移动应用使用 #name 格式。
  • 已归档的频道 仍然可以添加新成员(POST /channels/{channel_id}/members),新成员可以阅读历史记录。但任何人都无法在其中发帖、添加表情回应、固定消息、置顶消息、重命名频道或更改成员角色。
  • 内联回复:包含 "props": {"reply_to": "<post id>"} 的帖子会引用同一频道中的该消息。添加 "reply_to_mention": false 可避免通知原作者。服务器会在 metadata.reply_to 中填入被引用消息的简短描述。
  • 剧透:"props": {"spoiler": true} 会隐藏帖子的文本和文件,直到读者选择显示。
  • 忽略和屏蔽是偏好设置:类别为 user_block,名称为对方的 ID,值为 ignore 或 block。私信是否作为请求等待,取决于你的频道成员设置中的 notify_props.dm_request(pending、accepted 或 dismissed)。
  • 消息附件可以包含 stages 和 chart:请参阅 卡片。

移动话题

POST /posts/{post_id}/move_to_channel 将话题、其第一条消息及所有回复移动到同一社区的另一个频道。post_id 可以是第一条消息或任何回复。

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(必填):话题要移动到的目标频道。
  • flatten(默认为 false):设置为 true 时,回复消息将作为目标频道的普通消息,而不是话题。Fusion 的 Make it a channel 功能使用了此参数。

消息将保留其作者、时间、表情回应和文件。其副本带有 silent_notification 属性,因此不会通知任何人。原始消息将被删除。返回值为移动后的第一条消息,包含其新的 ID。

谁可以移动话题

  • 您必须能够读取该话题,并且能够在目标频道中发布消息。
  • 频道管理员、社区管理员、在频道中拥有 管理消息(delete_others_posts)权限的人员以及系统管理员可以移动任何话题。
  • 当两个频道均为公共频道,或者话题中的所有消息均由话题作者发布时,话题作者可以移动该话题。否则,返回值为 403,ID 为 api.post.move_thread.private_needs_moderator.app_error。
  • 两个频道必须位于同一社区(不能是私信或群组消息)、互不相同且未归档。系统消息和阅后即焚消息无法移动。
  • 长度超过 WranglerSettings.MoveThreadMaxCount 条消息(默认为 100;0 表示无限制)的话题将被拒绝。

指针和笔记

移动操作会在旧频道中留下两条类型为 system_wrangler 的系统消息:一条是指向新位置的指针(“一个包含 13 条消息的话题已移动到 #target”),另一条是目标频道中的笔记(“此话题从 #source 移动而来”),位于移动后的话题下方,或者在使用 flatten 时位于其消息之后。除了消息文本外,它们的属性允许客户端将频道名称渲染为链接:

  • 指针:包含 MovedThreadPermalink、NumMessages、MovedThreadChannelId、MovedThreadTeamName、MovedThreadPostId,以及针对公共目标频道的 MovedThreadChannelName 和 MovedThreadChannelDisplayName。
  • 笔记:包含 MovedFromChannelId、MovedFromTeamName、MovedFromPostId(指向指针)、MovedFromPermalink,以及针对公共源频道的 MovedFromChannelName 和 MovedFromChannelDisplayName。

两者还包含 TranslationID。私有频道的名称会被省略:读者看到的是“另一个频道”。Mattermost 自身的 POST /posts/{post_id}/move 在 Antimatter 中保持关闭;请使用此端点。

模型连接

模型连接是 AI 提供商的地址和密钥,由管理员进行配置。服务器的 AI 功能以及调用 AI 服务的应用程序都会使用这些连接。系统管理员管理服务器范围的连接(manage_ai_connections);社区管理员管理其社区自己的连接(manage_team_ai_connections)。密钥是只写的:任何响应都不会包含密钥,仅返回 api_key_set 和 api_key_last4。

端点功能说明
GET /ai/connections?scope=您管理的连接。scope 为 server 或社区 ID;如果不指定,则返回您管理的所有范围的连接。
POST /ai/connections添加连接(返回 201)。
POST /ai/connections/test, POST /ai/connections/models在保存之前测试连接或列出其模型。请求体与添加连接时相同;不会存储任何数据。
GET、PATCH(或 PUT)、DELETE /ai/connections/{connection_id}读取、修改或删除连接。补丁(Patch)仅包含需要更改的字段;如果省略 api_key,则保留现有密钥。
POST /ai/connections/{connection_id}/test测试已保存的连接:{"ok", "error", "status_code", "latency_ms", "model_count"}。
GET /ai/connections/{connection_id}/models该提供商提供的模型。
GET /ai/connections/{connection_id}/usage本月的请求数和令牌数。
GET /ai/connections/usable?channel_id=&team_id=面向任何已登录用户:显示您在该处可用的连接,包括其模型和功能,但绝不包含密钥或地址。

新连接的请求体:

{
  "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 或 anthropic。scope 默认为 server;如果指定社区 ID,则成为该社区的连接。在 limits 中,0 表示无限制;每分钟请求数在每台服务器上单独计数,令牌预算按整个集群的日历月(UTC)计算。sharing 指定哪些社区可以使用服务器范围的连接(私信和群组消息需要 all_teams),audience 指定谁可以使用该连接;访客仅在 allow_guests 为真时可用。更改会写入审计日志。管理员指南解释了这些设置:请参阅 AI。