REST API

Все действия в приложениях Antimatter выполняются через один и тот же HTTP API, поэтому ваши скрипты могут делать то же самое. На этой странице описано, как аутентифицироваться, какие ресурсы используются чаще всего, как выглядят ответы и ошибки, а также как получать события в реальном времени через WebSocket.

Основы

  • Базовый URL: https://chat.example.com/api/v4. Все перечисленные ниже конечные точки указаны относительно него.
  • Формат: вход и выход в формате JSON, за исключением загрузки файлов (multipart). Отправляйте Content-Type: application/json.
  • Идентификаторы: у каждого объекта есть id длиной 26 символов. Время указывается в миллисекундах с момента Unix epoch (create_at, update_at, delete_at; 0 означает «не удалено»).
  • Сообщества — это команды: в API они называются teams и имеют team_id.
  • Себя: везде, где в пути используется user_id, me означает текущего пользователя, например GET /users/me.

Полный список конечных точек находится в коде сервера: маршруты объявлены в server/channels/api4/*.go, а типы — в server/public/model. Описание OpenAPI в папке api/v4/source репозитория охватывает конечные точки, унаследованные от 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. Запрос, аутентифицированный cookie, должен также передавать заголовок X-CSRF-Token для всех методов, кроме GET; токен в заголовке Authorization не требует заголовка CSRF.

Приложения OAuth 2.0

Чтобы позволить пользователям входить в ваше приложение с помощью аккаунта Antimatter, зарегистрируйте приложение OAuth 2.0. Antimatter в этом случае выступает провайдером OAuth:

  1. Включить поставщика услуг OAuth 2.0 должен быть включён (Системная консоль › Интеграции › Управление интеграцией, ServiceSettings.EnableOAuthServiceProvider, включено по умолчанию).
  2. Пользователь с правом управления приложениями OAuth (по умолчанию — системные администраторы) нажимает на свой аватар, затем выбирает Интеграции › Приложения OAuth 2.0 и добавляет приложение с URL обратного вызова. Он получает client ID и client secret.
  3. Ваше приложение перенаправляет пользователей на https://chat.example.com/oauth/authorize с параметрами response_type=code, client_id, redirect_uri и state. Поддерживается PKCE с code_challenge_method=S256.
  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" (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 (идентификатор сообщения). Ответ возвращается в виде списка сообщений: {"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.

Библиотеки для клиентов

  • Перейти: model.Client4 в server/public/model (импорт github.com/mattermost/mattermost/server/public/model). model.NewAPIv4Client(url), затем client.SetToken(token); model.NewWebSocketClient4(wsURL, token) отслеживает события. Копия Antimatter добавляет методы для собственных конечных точек, такие как UpdateMessageRequest, PutPostOnTop и ExportChannel.
  • TypeScript: класс Client4 из @mattermost/client в 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
Подключения моделей, провайдеры ИИ, которые используют функции и приложения ИИ сервера: см. Подключения моделей.
GET /channels/{channel_id}/message_actions
POST /posts/{post_id}/message_actions/{hook_id}
GET, POST /hooks/outgoing/{hook_id}/icon
Действия с сообщениями: исходящие вебхуки, которые пользователи запускают из меню сообщения. См. Действия с сообщениями.

Новые поля

  • Сообщества (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).
  • The default channel of a new community is general (General), not town-square. The 0.9.0 upgrade renames each community's town-square to general, keeping its ID, unless the community already has a general channel. Old names keep working: GET /teams/{team_id}/channels/name/town-square, GET /teams/name/{team}/channels/name/town-square and an incoming webhook's channel give the community's default channel when no channel has that name. Use default_channel_id, or the name general, in new code.
  • Ссылки на каналы: ссылка #channel-name указывает на канал, как и ~channel-name. Свойство channel_mentions сообщения перечисляет каналы, на которые ссылается сообщение любым из этих способов; #word, не совпадающий с именем канала, остаётся хэштегом. Веб-интерфейс и мобильное приложение Antimatter используют формат #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 для этого используется пункт Сделать каналом.

Сообщения сохраняют своих авторов, время создания, реакции и файлы. Их копии содержат свойство silent_notification, поэтому уведомления не отправляются. Исходные сообщения удаляются. В ответе возвращается первое сообщение после перемещения с новым ID.

Кто может перемещать обсуждение

  • Вы должны иметь право просматривать обсуждение и публиковать сообщения в целевом канале.
  • Администраторы канала, администраторы сообщества, пользователи с правом Управление сообщениями (delete_others_posts) в канале и системные администраторы могут перемещать любое обсуждение.
  • Автор обсуждения может переместить его, если оба канала публичные, либо если все сообщения в обсуждении принадлежат ему. В противном случае возвращается ответ 403 с идентификатором 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 отключён; используйте этот эндпоинт.

Подключения моделей

Соединение с моделью — это адрес и ключ провайдера ИИ, настроенные администратором. Они используются серверными функциями ИИ и приложениями, обращающимися к сервису ИИ. Системные администраторы управляют общесерверными соединениями (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}Чтение, изменение или удаление соединения. Патч содержит только изменяемые поля; не указывайте 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; если указан идентификатор сообщества, подключение становится его собственным. В limits значение 0 означает отсутствие ограничений: запросы в минуту считаются на каждом сервере, а бюджеты токенов — за календарный месяц (UTC) для всего кластера. sharing определяет, какие сообщества могут использовать серверное подключение (для личных и групповых сообщений требуется all_teams), а audience — кто может им пользоваться; гости — только при allow_guests. Изменения записываются в журнал аудита. Настройки описаны в руководстве администратора: см. ИИ.