Interfejs REST API

Wszystkie operacje wykonywane w aplikacjach Antimatter przechodzą przez ten sam interfejs HTTP, więc mogą je wykonywać również Twoje skrypty. Ta strona wyjaśnia, jak się uwierzytelniać, jakie zasoby będziesz używać najczęściej, jak wyglądają odpowiedzi i błędy oraz jak śledzić zdarzenia na żywo za pomocą WebSocket.

Podstawy

  • Adres URL bazowy: https://chat.example.com/api/v4. Wszystkie poniższe punkty końcowe są względne wobec tego adresu.
  • Format: dane wejściowe i wyjściowe w formacie JSON, z wyjątkiem przesyłania plików (multipart). Wysłanie nagłówka Content-Type: application/json jest wymagane.
  • Identyfikatory: każdy obiekt ma 26-znakowy id. Czas jest wyrażony w milisekundach od epoki Unix (create_at, update_at, delete_at; wartość 0 oznacza „nie usunięto”).
  • Wspólnoty to zespoły: interfejs API nazywa je teams i używa identyfikatora team_id.
  • sam: wszędzie tam, gdzie ścieżka przyjmuje user_id, me oznacza zalogowanego użytkownika, np. GET /users/me.

Pełna lista punktów końcowych znajduje się w kodzie serwera: trasy są zadeklarowane w server/channels/api4/*.go, a typy w server/public/model. Opis OpenAPI w folderze api/v4/source w repozytorium obejmuje punkty końcowe dziedziczone z Mattermost oraz niektóre dodatki Antimatter: przenoszenie wątków, połączenia modeli i akcje na wiadomościach. Pozostałe są dostępne tylko w kodzie.

Uwierzytelnianie

Każde żądanie, z wyjątkiem logowania i kilku publicznych punktów końcowych, wymaga tokena w nagłówku Authorization:

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

Akceptowane są trzy rodzaje tokenów: osobiste tokeny dostępu, tokeny sesji i tokeny dostępu OAuth. Akceptowany jest również format Authorization: Token …. Tokeny w parametrach zapytania (?access_token=) są akceptowane wyłącznie dla tokenów dostępu OAuth.

Osobiste tokeny dostępu (zalecane dla skryptów)

Osobisty token dostępu działa w Twoim imieniu, nie wygasa, chyba że ustawisz dla niego datę wygaśnięcia, i może zostać odwołany w dowolnym momencie. Osobiste tokeny dostępu są domyślnie wyłączone:

  1. Administrator systemu włącza Włącz osobiste tokeny dostępu w Konsola systemu › Integracje › Zarządzanie Integracjami (ServiceSettings.EnableUserAccessTokens, AM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS).
  2. Dla każdej osoby, która może tworzyć tokeny, administrator systemu otwiera Konsola systemu › Zarządzanie użytkownikiem › Użytkownicy, wybiera Zarządzanie rolami w menu danej osoby i zaznacza Allow this account to generate personal access tokens. Administratorzy systemu mogą zawsze je tworzyć.
  3. Osoba otwiera swój awatar w prawym górnym rogu, a następnie Profil & ustawienia › Bezpieczeństwo. W sekcji Personalne tokeny dostępu opisuje, do czego służy token, i wybiera Utwórz nowy token. Token jest wyświetlany tylko raz: skopiuj go.

Z użyciem API: POST /users/{user_id}/tokens z {"description": "backup script", "expires_at": 0} (expires_at w milisekundach; 0 oznacza brak wygaśnięcia). Pole token w odpowiedzi to sekret; jego id służy do odwołania tokena (POST /users/tokens/revoke z {"token_id": "…"}). Z wiersza poleceń: amctl token generate <user> <description>.

Administrator systemu może ustawić limit czasu ważności tokenów za pomocą Maksymalny okres ważności osobistego tokenu dostępu (dni) na tej samej stronie Konsoli systemu.

Tokeny sesji

Logowanie hasłem zwraca token sesji w nagłówku odpowiedzi Token, będący tego samego rodzaju sesją, której używają aplikacje:

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

Dodaj "token": "123456" dla konta z uwierzytelnianiem wieloskładnikowym. Sesje wygaszają zgodnie z ustawieniami sesji serwera, a punkt końcowy logowania jest ograniczany (5 żądań na sekundę). POST /users/logout kończy sesję.

Przeglądarki uwierzytelniają się za pomocą ciasteczka. Żądanie uwierzytelnione za pomocą ciasteczka musi również wysyłać nagłówek X-CSRF-Token dla wszystkich metod innych niż GET; token w nagłówku Authorization nie wymaga nagłówka CSRF.

Aplikacje OAuth 2.0

Aby umożliwić użytkownikom logowanie do Twojej aplikacji za pomocą konta Antimatter, zarejestruj aplikację OAuth 2.0. Antimatter staje się wtedy dostawcą OAuth:

  1. Opcja Włącz dostawcę usługi OAuth 2.0 musi być włączona (Konsola systemu › Integracje › Zarządzanie integracjami, ServiceSettings.EnableOAuthServiceProvider, domyślnie włączona).
  2. Osoba z odpowiednimi uprawnieniami do zarządzania aplikacjami OAuth (domyślnie administratorzy systemu) otwiera swój awatar, a następnie Integracje › Aplikacje OAuth 2.0 i dodaje aplikację wraz z adresami URL wywołań zwrotnych. Otrzymuje identyfikator klienta (client ID) i sekret klienta (client secret).
  3. Twoja aplikacja przekierowuje użytkowników na https://chat.example.com/oauth/authorize z parametrami response_type=code, client_id, redirect_uri oraz state. Obsługiwane jest PKCE z code_challenge_method=S256.
  4. Wymienia kod na POST https://chat.example.com/oauth/access_token (najpierw grant_type=authorization_code, a następnie refresh_token do odnowienia) i wywołuje API z nagłówkiem Authorization: Bearer <access token>.

Użytkownicy mogą zobaczyć aplikacje, którym udzielili dostępu, w sekcji Profil i ustawienia › Bezpieczeństwo › Autoryzowane aplikacje.

Główne zasoby

CoPunkty końcowe
Ty i inni użytkownicyGET /users/me, GET /users/{user_id}, GET /users/username/{username}, POST /users/ids (lista identyfikatorów), POST /users/search, GET /users?in_team=…&in_channel=…
SpołecznościGET /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
KanałyGET /users/me/teams/{team_id}/channels, GET /teams/name/{team}/channels/name/{channel}, POST /channels, POST /channels/direct (dwa identyfikatory użytkowników), POST /channels/group, POST /channels/{channel_id}/members
Wiadomości (posty)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
ReakcjePOST /reactions z ciałem {"user_id", "post_id", "emoji_name"}, DELETE /users/{user_id}/posts/{post_id}/reactions/{emoji_name}
PlikiPOST /files (multipart: channel_id i files), a następnie umieszczenie zwróconych identyfikatorów w polu file_ids wiadomości; GET /files/{file_id}
PreferencjeGET /users/me/preferences, PUT /users/me/preferences
Integracje/hooks/incoming, /hooks/outgoing, /commands, /bots, /oauth/apps (patrz kolejne strony)
SerwerGET /system/ping (stan zdrowia, bez tokena), GET /config/client?format=old (ustawienia widoczne dla klientów)

Odesłanie wiadomości

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

Wiadomości są formatowane w Markdown. Aby odpowiedzieć w wątku, dodaj "root_id" (identyfikator pierwszej wiadomości wątku), "file_ids" dla plików oraz "props" dla załączników i innych dodatków (patrz Załączniki).

Znajdowanie identyfikatora kanału

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

Nazwy to te z linku do kanału: /lumen-labs/channels/ops-alerts.

Paginacja

Punkty końcowe zwracające listy akceptują parametry page (od 0) i per_page (domyślnie 60, maksymalnie 200). Kontynuuj żądanie kolejnych stron, dopóki zwrócona strona nie będzie krótsza niż per_page.

Wiadomości w kanale (GET /channels/{channel_id}/posts) akceptują również since (czas: wszystko zmienione od tego momentu) lub before i after (ID wiadomości). Zwracane są jako lista wiadomości: {"order": [IDs, newest first], "posts": {ID: post}, "next_post_id", "prev_post_id"}.

Błędy

Błędy używają standardowych kodów statusu HTTP (400 błędne dane wejściowe, 401 brak zalogowania, 403 brak uprawnień, 404 nie znaleziono, 501 funkcja wyłączona) oraz treści JSON:

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

Używaj id w swoim kodzie: message jest tłumaczone na język użytkownika. detailed_error jest puste, chyba że serwer działa w trybie deweloperskim (ServiceSettings.EnableDeveloper). Podaj request_id administratorowi serwera: znajduje się on w logach.

Ograniczenia przepustowości

Ograniczenia prędkości są domyślnie wyłączone. Gdy administrator systemu je włączy (Konsola systemu › Środowisko › Ograniczenia Prędkości), każdy klient otrzymuje domyślnie 10 żądań na sekundę z pulą 100, liczone po adresie IP lub po użytkowniku. Odpowiedzi zawierają X-RateLimit-Limit, X-RateLimit-Remaining i X-RateLimit-Reset; przy przekroczeniu limitu serwer odpowiada 429 z nagłówkiem Retry-After w sekundach. Niektóre punkty końcowe, takie jak logowanie, mają własny limit niezależnie od ustawień.

Pisz skrypty, które czekają i ponawiają żądania przy 429, oraz unikaj odpytywania: korzystaj z WebSocket.

Strumień zdarzeń WebSocket

Połącz się z wss://chat.example.com/api/v4/websocket. Uwierzytelnij się za pomocą nagłówka Authorization: Bearer w żądaniu połączenia lub wysyłając to jako pierwsze wiadomość:

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

Serwer wysyła zdarzenie hello, a następnie zdarzenia dla wszystkiego, co masz uprawnienie zobaczyć:

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

Zauważ, że data.post to post jako ciąg JSON. Częste zdarzenia: posted, post_edited, post_deleted, reaction_added, reaction_removed, typing, status_change, user_added, direct_added. Aplikacje wysyłają własne zdarzenia, nazwane custom_<app id>_<event>.

Możesz również wysyłać akcje, każda z rosnącym seq: user_typing z {"channel_id", "parent_id"} lub get_statuses. Połącz się ponownie, gdy połączenie się zerwie, i pobierz to, co przegapiłeś, za pomocą since (patrz Paginacja).

Antimatter dodaje te zdarzenia: community_attributes_updated, member_timeouts_updated, post_put_on_top, post_removed_from_top, server_categories_updated, channel_overrides_updated oraz category_overrides_updated.

Biblioteki klienckie

  • Przejdź: model.Client4 w server/public/model (import github.com/mattermost/mattermost/server/public/model). model.NewAPIv4Client(url), a następnie client.SetToken(token); model.NewWebSocketClient4(wsURL, token) śledzi zdarzenia. Kopia Antimattera dodaje metody dla własnych punktów końcowych, takich jak UpdateMessageRequest, PutPostOnTop i ExportChannel.
  • TypeScript: klasa Client4 pakietu @mattermost/client w webapp/platform/client, z typami w webapp/platform/types. Kopia w repozytorium Antimattera zawiera punkty końcowe Antimattera; pakiet opublikowany na npm przez Mattermosta ich nie zawiera.
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)
}

Co Antimatter dodaje do API

Te punkty końcowe i pola nie istnieją na serwerze Mattermost. Wszystkie wymagają sesji.

Punkt końcowyOpis
GET /teams/{team_id}/user_attributes/fields
POST na tej samej ścieżce
PATCH, DELETE …/fields/{field_id}
Atrybuty społeczności: pola profilu, które administratorzy społeczności definiują dla jej członków (do 20). Członkowie mogą je odczytywać; administratorzy społeczności je tworzą i modyfikują.
GET /teams/{team_id}/members/{user_id}/user_attributes
PATCH na tej samej ścieżce
POST /teams/{team_id}/user_attributes/values/search
Wartości atrybutów członka, w formacie {field_id: value}. Członkowie ustawiają własne (z wyjątkiem zarządzanych przez administratorów); administratorzy mogą ustawiać wartości dla dowolnego użytkownika. Wyszukiwanie przyjmuje {"user_ids": [...]} (do 200).
GET /teams/{team_id}/timeouts
PUT /teams/{team_id}/members/{user_id}/timeout
GET /teams/{team_id}/members/{user_id}/moderation
Bloki czasowe: {"until": time, "reason": "…"} uniemożliwia użytkownikowi publikowanie, reagowanie i przesyłanie plików w społeczności do podanego czasu (maksymalnie 28 dni); "until": 0 usuwa blok. Widok moderacji pokazuje, kiedy użytkownik dołączył i jak dużo ostatnio opublikował.
GET /channels/{channel_id}/on_top
PUT, DELETE /posts/{post_id}/on_top
Wiadomości przypięte na górze kanału (do 10). {"duration": ms}, maksymalnie 30 dni; wartość 0 oznacza, że pozostaje do momentu usunięcia. Lista jest widoczna dla każdego, kto może czytać kanał. Przypięcie wiadomości na górze lub jej odprzypięcie wymaga uprawnień do publikowania w kanale oraz, w przypadku kanału społeczności, bycia administratorem kanału lub społeczności lub posiadania uprawnień Zarządzanie wiadomościami (delete_others_posts) w tym kanale. W przypadku wiadomości bezpośredniej lub grupowej mogą to zrobić jej członkowie. W kanałach zarchiwizowanych operacja jest odrzucana.
POST /channels/{channel_id}/message_requestDziała na własnym żądaniu wiadomości: {"action": "accept"}, "dismiss" lub "request", aby przywrócić konwersję do listy żądań. Zwraca członka kanału.
GET, PUT /channels/{channel_id}/overrides
GET /channels/{channel_id}/overrides/effective
GET, PUT /channels/{channel_id}/role_access
Określa, co rola lub osoba może wykonywać w kanale, oraz które role mają dostęp do kanału z ograniczeniami ról.
GET, PUT /teams/{team_id}/server_categories
GET, PUT …/server_categories/overrides
Wspólne kategorie kanałów społeczności oraz nadpisanie ustawień, które mają zastosowanie do wszystkich kanałów w danej kategorii.
GET /groups/{group_id}/team_role
PUT …/team_role/permissions
Uprawnienia przyznawane przez rolę społeczności.
POST /teams/{team_id}/autolinks/previewTestuje reguły automatycznych linków społeczności na podanym tekście.
POST /channels/{channel_id}/export?format=csv&since=&until=Pobiera wiadomości z kanału w formacie CSV (do 50 000) dla osób, które mają uprawnienia do odczytu tego kanału.
GET /posts/{post_id}/speechWiadomość odczytana na głos jako plik audio, gdy skonfigurowano funkcję konwersji tekstu na mowę.
POST /posts/{post_id}/move_to_channelPrzenosi wątek do innego kanału w tej samej społeczności: zobacz Przenoszenie wątku.
GET, POST /ai/connections
GET, PATCH, DELETE /ai/connections/{connection_id}
GET /ai/connections/usable
Połączenia z modelami, dostawcy AI używani przez funkcje AI serwera i aplikacje: zobacz Połączenia z modelami.
GET /channels/{channel_id}/message_actions
POST /posts/{post_id}/message_actions/{hook_id}
GET, POST /hooks/outgoing/{hook_id}/icon
Akcje wiadomości: wychodzące webhooki uruchamiane przez użytkowników z menu wiadomości. Zobacz Akcje wiadomości.

Nowe pola

  • Społeczności (Team oraz PUT /teams/{team_id}/patch): all_channels_membership (każdy jest członkiem każdego kanału, do którego ma dostęp), default_channel_id (kanał, w którym lądują nowi użytkownicy; pusta wartość oznacza kanał General), kind (community lub workplace: w przypadku miejsca pracy administratorzy kanałów mogą również archiwizować swoje kanały; nowe społeczności otrzymują wartość TeamSettings.DefaultCommunityKind serwera, domyślnie community), member_list_view (sposób grupowania członków w liście: choice, wartość domyślna, pozwala każdemu członkowi wybrać; roles lub teams narzuca grupowanie), hide_role_colors, require_team_badges, onboarding i autolinks (obie jako ciągi JSON).
  • Domyślnym kanałem nowej społeczności jest general (General), a nie town-square. Aktualizacja do wersji 0.9.0 zmienia nazwę town-square każdej społeczności na general, zachowując jej identyfikator, chyba że w społeczności istnieje już kanał general. Stare nazwy nadal działają: GET /teams/{team_id}/channels/name/town-square, GET /teams/name/{team}/channels/name/town-square oraz pole channel przychodzącego webhooka zwracają domyślny kanał społeczności, gdy nie ma kanału o tej nazwie. W nowym kodzie używaj default_channel_id lub nazwy general.
  • Linki do kanałów: #channel-name linkuje kanał, tak jak ~channel-name. Właściwość channel_mentions posta zawiera listę kanałów powiązanych w którykolwiek z tych sposobów; #word, który nie wskazuje żadnego kanału, pozostaje tagiem. Interfejs webowy Antimatter i aplikacja mobilna zapisują #name.
  • Zarchiwizowane kanały nadal mogą przyjmować nowych członków (POST /channels/{channel_id}/members), którzy mogą czytać ich historię. Nikt nie może tam publikować, reagować, przypinać, umieszczać wiadomości na górze, zmieniać nazwy kanału ani zmieniać ról jego członków.
  • Odpowiedzi inline: wiadomość z "props": {"reply_to": "<post id>"} cytuje wskazaną wiadomość z tego samego kanału. Dodaj "reply_to_mention": false, aby nie powiadamiać autora cytowanej wiadomości. Serwer wypełnia pole metadata.reply_to krótkim opisem cytowanej wiadomości.
  • Ujawnienia (spoilers): "props": {"spoiler": true} ukrywa tekst i pliki wiadomości, aż czytelnik zdecyduje się je wyświetlić.
  • Ignorowanie i blokowanie to preferencje: kategoria user_block, nazwana według identyfikatora drugiej osoby, z wartością ignore lub block. Czy wiadomość bezpośrednia czeka jako żądanie, zależy od pola notify_props.dm_request w profilu członka kanału (pending, accepted lub dismissed).
  • Załączniki do wiadomości mogą zawierać stages oraz chart: zobacz Karty.

Przenoszenie wątku

POST /posts/{post_id}/move_to_channel przenosi wątek, jego pierwszą wiadomość oraz wszystkie odpowiedzi do innego kanału tej samej społeczności. post_id może być pierwszą wiadomością lub dowolną odpowiedzią.

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 (wymagane): kanał, do którego przenoszony jest wątek.
  • flatten (domyślnie false): wartość true sprawia, że odpowiedzi stają się zwykłymi wiadomościami docelowego kanału, a nie wątkiem. Funkcja Make it a channel w Fusion używa tego parametru.

Wiadomości zachowują swoich autorów, znaczniki czasu, reakcje i pliki. Ich kopie zawierają właściwość silent_notification, dzięki czemu nie wysyłają powiadomień. Oryginalne wiadomości są usuwane. Odpowiedzią jest przeniesiona pierwsza wiadomość z nowym identyfikatorem.

Kto może przenosić wątki

  • Musisz mieć możliwość odczytu wątku oraz publikowania w docelowym kanale.
  • Administratorzy kanału, administratorzy społeczności, osoby z uprawnieniem Zarządzanie wiadomościami (delete_others_posts) w kanale oraz administratorzy systemu mogą przenosić dowolny wątek.
  • Autor wątku może go przenieść, gdy oba kanały są publiczne, lub gdy wszystkie wiadomości w wątku należą do niego. W przeciwnym razie odpowiedzią jest 403 z identyfikatorem api.post.move_thread.private_needs_moderator.app_error.
  • Oba kanały muszą należeć do tej samej społeczności (nie mogą to być wiadomości bezpośrednie ani grupowe), muszą być różne i niearchiwizowane. Nie można przenosić wiadomości systemowych ani wiadomości spalanych po odczytaniu.
  • Wątek dłuższy niż WranglerSettings.MoveThreadMaxCount wiadomości (domyślnie 100; 0 oznacza brak limitu) jest odrzucany.

Wskazówka i notatka

Przeniesienie zostawia dwie wiadomości systemowe typu system_wrangler: wskazówkę w starym kanale („Wątek z 13 wiadomościami został przeniesiony do #target”) i notatkę w docelowym („Ten wątek został przeniesiony z #source”), pod przeniesionym wątkiem lub, w przypadku flatten, po jego wiadomościach. Oprócz treści wiadomości, ich właściwości pozwalają klientowi wyświetlić nazwę kanału jako link:

  • Wskazówka: MovedThreadPermalink, NumMessages, MovedThreadChannelId, MovedThreadTeamName, MovedThreadPostId oraz, dla publicznego kanału docelowego, MovedThreadChannelName i MovedThreadChannelDisplayName.
  • Notatka: MovedFromChannelId, MovedFromTeamName, MovedFromPostId (wskazówka), MovedFromPermalink oraz, dla publicznego kanału źródłowego, MovedFromChannelName i MovedFromChannelDisplayName.

Obie mają również TranslationID. Nazwa kanału prywatnego jest pomijana: czytelnicy widzą „inny kanał”. Własne POST /posts/{post_id}/move Mattermost jest wyłączone w Antimatter; użyj tego punktu końcowego.

Połączenia z modelami

Połączenie modelu to adres i klucz dostępu dostawcy AI, konfigurowane przez administratora. Funkcje AI serwera oraz aplikacje wywołujące usługę AI z nich korzystają. Administratorzy systemu zarządzają połączeniami na poziomie serwera (manage_ai_connections); administratorzy społeczności zarządzają połączeniami swojej społeczności (manage_team_ai_connections). Klucze są tylko do zapisu: żadna odpowiedź nie zawiera klucza, tylko api_key_set i api_key_last4.

Punkt końcowyOpis
GET /ai/connections?scope=Połączenia, którymi zarządzasz. scope to server lub identyfikator społeczności; bez niego wszystkie zakresy, którymi zarządzasz.
POST /ai/connectionsDodaje połączenie (odpowiedź 201).
POST /ai/connections/test, POST /ai/connections/modelsTestuje połączenie lub wypisuje jego modele przed zapisaniem. Ten sam korpus co przy dodawaniu; nic nie jest przechowywane.
GET, PATCH (lub PUT), DELETE /ai/connections/{connection_id}Odczytuje, zmienia lub usuwa połączenie. Łatka (patch) zawiera tylko pola do zmiany; pomiń api_key, aby zachować klucz.
POST /ai/connections/{connection_id}/testTestuje zapisane połączenie: {"ok", "error", "status_code", "latency_ms", "model_count"}.
GET /ai/connections/{connection_id}/modelsModele oferowane przez dostawcę.
GET /ai/connections/{connection_id}/usageŻądania i tokeny z tego miesiąca.
GET /ai/connections/usable?channel_id=&team_id=Dla każdego zalogowanego użytkownika: połączenia, których może tam używać, wraz z ich modelami i możliwościami, nigdy z kluczami ani adresami.

Treść nowego połączenia:

{
  "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 to openai_compatible lub anthropic. scope domyślnie ma wartość server; identyfikator społeczności sprawia, że staje się to połączeniem tej społeczności. W limits wartość 0 oznacza brak limitu; żądania na minutę są liczone na każdym serwerze, a budżety tokenów na kalendarzowy miesiąc (UTC) dla całego klastra. sharing określa, które społeczności mogą używać połączenia na poziomie serwera (wiadomości bezpośrednie i grupowe wymagają all_teams), a audience kto może je używać; goście tylko przy allow_guests. Zmiany są zapisywane w dzienniku audytu. Przewodnik administratora wyjaśnia ustawienia: zobacz AI.