PartyFlow

Концепция: Bots#

Bot account — именованный участник канала, от лица которого работает ваша интеграция. Он отправляет сообщения, читает историю (для AI-ботов), регистрирует кастомные slash-команды и обрабатывает нажатия кнопок. В отличие от webhook'а бот — это субъект в канале: у него есть имя, аватар, упоминания (@bot-name) и membership.

Эта страница — концепция: зачем нужен бот, чем он отличается от webhook'а, какой у него жизненный цикл и как с ним работать безопасно. Всю техническую часть (endpoints, параметры, примеры запросов) смотрите в reference/bot-rest-api.md.


Bot vs Webhook#

Коротко: webhook — труба, бот — участник.

Incoming webhook Bot
Идентичность в канале Анонимная, либо настроенный для webhook'а display name Именованный участник с @mention, аватаром, постоянным ID
Auth Token в URL + опциональный HMAC Bearer token в Authorization header
Направление Только входящие POST → в канал Двустороннее: отправка, чтение, interactive, slash-команды
Membership Автоматически привязан к одному каналу Отдельная операция /join, бот может состоять в нескольких каналах
Используют Grafana, GitLab CI, Sentry, Prometheus Alertmanager Standup-боты, AI-ассистенты, approval-боты, deployment-боты

Если цель — просто доставить alert из внешнего сервиса, бот не нужен: используйте webhook. Бот нужен, когда интеграции надо:

  • Отвечать пользователям в канале как именованный участник (@standup-bot).
  • Загружать и прикреплять файлы к сообщениям или final Agent Run updates.
  • Читать историю канала (summarize, search, index).
  • Регистрировать собственные slash-команды (/deploy, /review).
  • Показывать кнопки / select menus / модалки с двусторонней логикой.

Lifecycle#

create → store token → invite to channels → send / read messages → (optional) regenerate token → (optional) disable

1. Create#

Админ space'а создаёт бота в UI: Integrations → Bots → Create. Указывает:

  • name — системное имя (^[a-z0-9_-]{1,32}$), по нему бота можно @упоминать.
  • display_name — что показывается в канале.
  • description, avatar_url — опционально.

При создании PartyFlow возвращает raw-токен формата fri_bot_<base64url> и показывает его один раз. Token — SHA-256 hash которого хранится в БД; plaintext после closing диалога не восстановить.

2. Store token#

Сохранить токен в secret manager (AWS Secrets Manager, HashiCorp Vault, Doppler, GitHub Actions secrets). Не хардкодить в исходниках, не коммитить в git.

Минимум прав для runtime: read-only доступ к секрету у того процесса, где крутится бот. Ротация — через UI (см. ниже).

3. Invite to channels#

Бот не попадает в каналы автоматически. Для каждого канала, где он должен работать, вызываем:

POST /api/v1/bot/conversations/{conversation_id}/join

Идемпотентно. Повторный вызов возвращает HTTP 200 без дубликатов membership. Admin также может добавить бота вручную через UI канала.

Бот может состоять в неограниченном числе каналов одного space'а. Cross-space membership невозможно — если бот принадлежит space A, он не может быть приглашён в канал space B (возвращается HTTP 400 space mismatch).

4. Send / Read messages#

  • Send: POST /api/v1/bot/messages — отправка сообщения от имени бота. Поддерживается thread reply через thread_id, per-message override display_name / display_avatar_url.
  • Bot Files: POST /api/v1/bot/files/upload_sessions → direct upload → POST /api/v1/bot/files/{file_id}/confirm — загрузка файла для top-level attachments в сообщении или terminal Agent Run update.
  • Agent Runs: POST /api/v1/bot/agent_runs + PATCH /api/v1/bot/agent_runs/{run_id} — для долгих AI-ответов: сразу создаёт placeholder-сообщение и затем обновляет его безопасными статусами.
  • Execution Tasks: POST /api/v1/bot/execution_tasks — task widget для долгой работы агента, где пользователи видят состояние, final artifacts и review/acceptance.
  • Read: GET /api/v1/channels/{conversation_id}/messages — чтение истории канала с курсорами before_msg_index / after_msg_index / around_msg_index, фильтром thread_id, delta-sync через updated_since. Privacy-фильтр применяется: ботам не возвращаются read_receipts, edit_history, delivery_status, parent_preview, mention_details.
  • DM: POST /api/v1/bot/dm — создание direct-чата с пользователем, GET /api/v1/bot/stream — SSE-поток для real-time получения сообщений из DM. См. раздел DM с ботом ниже.

Полный справочник — в reference/bot-rest-api.md.

5. Regenerate token#

Если токен утёк (попал в лог, git, скриншот) — Regenerate token в UI. Старый token становится невалидным немедленно (без grace period): следующий запрос с ним вернёт HTTP 401.

Плюс-эффект: после regenerate cache токена в PartyFlow инвалидируется, так что даже если процесс с старым токеном где-то ещё живёт, доступ у него пропадает в пределах секунд.

6. Disable#

Admin может отключить бота (toggle is_active: false) без удаления. После этого:

  • Все запросы с токенами бота возвращают HTTP 401.
  • Membership в каналах сохраняется, но сообщения от бота не проходят.
  • GET /api/v1/bot/me с тем же токеном тоже возвращает HTTP 401; поле is_active в успешном ответе нужно для self-check активного токена.

Включение обратно — тоже toggle в UI. Автоматического re-enable после fix'а на стороне бота не предусмотрено: policy делается явным действием админа.

Полное удаление бота — отдельная операция Delete bot, она сносит membership во всех каналах, revoke'ает все токены, удаляет запись.


Authentication#

Model#

  • Bot использует Bearer token в заголовке Authorization: Bearer fri_bot_<token> на каждом REST-запросе.
  • Токен хранится в БД PartyFlow как SHA-256 hash, не plaintext. Это значит — при компрометации БД токен не восстановить, только инвалидировать.
  • Токен связан с ботом one-to-many: у одного бота может быть несколько активных токенов (например, blue/green deploy), Regenerate ротирует все активные токены.
  • Валидация токена — timing-safe (hmac.Equal на hash), так что перебор по времени ответа не работает.

Если токен утёк#

  1. Немедленно Regenerate token в UI. Старый мёртв мгновенно.
  2. Обновить секрет в secret manager, перезапустить процесс бота с новым токеном.
  3. Посмотреть в логах space'а (admin UI), какие IP/UA использовали утёкший токен последние 24 часа — понять blast radius.
  4. Если бот проводил write-действия (отправлял сообщения, вступал в каналы): ревью audit-трейла канала. Webhook-подобная подделка через бота возможна только с валидным токеном, так что после regenerate новых write'ов быть не должно.

Bot token vs webhook signing secret#

Разные концепции, не путать:

  • Bot token (fri_bot_...) — identity. Доказывает, что запрос делает именно этот бот. Аналог пароля.
  • Webhook signing secret (whsec_...) — integrity. Доказывает, что тело запроса не изменили по дороге. Аналог TLS для HTTP, но на уровне приложения.

Бот использует только Bearer-token. HMAC-подпись бот проверяет только когда PartyFlow шлёт ему запросы (button click callback, custom slash callback, outgoing webhook) — тогда используется buttons_signing_secret / signing_secret webhook'а. См. concepts/security.md.


Membership#

Membership бота в канале — явная запись в таблице участников, такая же как у обычного user'а. Это значит:

  • Бот появляется в списке участников канала, в поиске по @mention, в autocomplete.
  • Напрямую из приглашения в один канал бот не получает доступ к другим каналам того же space'а.
  • is_active: false на боте — глобальный kill-switch, но не affects membership: после re-enable бот остаётся в тех же каналах.
  • Удаление бота (Delete bot) каскадом сносит membership во всех каналах.

Нет auto-rejoin. Если кто-то выкинул бота из канала (/kick @bot), он там больше не состоит и должен быть заново приглашён — бот сам себя обратно не вступит, даже если его код сейчас зовёт /join. Это осознанно: админы должны уметь выгнать бота, и бот не должен это обходить.


Read-scope#

Bot Read API позволяет боту получать историю канала под тем же Bearer auth, что и отправку. Используется для:

  • AI-summarizers: "перескажи последние 50 сообщений" → бот читает историю и публикует summary.
  • Long-memory агентов: бот хранит свой контекст о канале между запросами.
  • Indexing / search: бот экспортирует сообщения во внешний search engine.

Важное из privacy-policy:

  • Ботам не возвращаются: read_receipts, edit_history, delivery_status, parent_preview, mention_details. Эти поля — user-to-user metadata, которая не должна утекать в AI-индекс.
  • system_data.extra_json скрыто (может содержать device/IP). Если нужен конкретный typed атрибут — запрашивайте через platform team.
  • is_deleted: true — сообщение возвращается как tombstone (id/author/timestamps сохранены, text = "", attachments = []). Для audit, но контент больше не видно.

Rate limit: 10 запросов/минуту на канал × 100 запросов/минуту на бота.

Полный справочник: reference/bot-rest-api.md → Чтение истории канала.


AI agents и task widgets#

Для короткого ответа бот может просто отправить сообщение. Для долгой работы AI-агента лучше использовать две сущности вместе:

  • Agent Run показывает пользователю, что агент принял запрос и в каком он runtime-статусе: ищет контекст, вызывает инструмент, финализирует ответ.
  • Execution Task widget хранит долговечное состояние работы: title, description, task status, публичный результат, final artifacts и review flow.

Рекомендуемый порядок:

  1. Создайте execution_task в том же канале или треде, где пользователь дал запрос.
  2. Создайте Agent Run с top-level widget_id.
  3. Обновляйте Agent Run для runtime-статусов. Обновляйте task widget только для изменений, которые должны жить как состояние задачи.
  4. На terminal Agent Run (completed, failed, cancelled, expired) PartyFlow обновит связанный task и прикрепит только проверенные финальные файлы.

Ограничения безопасности:

  • Бот может мутировать только task widgets, созданные тем же bot actor.
  • space_id, bot_id и actor всегда выводятся из Bearer token, не из body.
  • В public state нельзя класть prompts, chain-of-thought, raw tool payloads, stack traces, приватные идентификаторы файлов, long-lived URLs или секреты.
  • agent_run.sequence и execution_task.sequence не взаимозаменяемы: храните оба значения отдельно.

Полный contract: reference/bot-rest-api.md → Execution Tasks.


DM с ботом#

Бот может участвовать в личных переписках (direct messages) — как с человеком, так и наоборот.

Как создаётся DM#

  • Пользователь создаёт DM через обычный UI чата: "New direct message" → выбирает бота из списка участников space'а. Бекенд определяет, что peer — бот, и создаёт direct-чат.
  • Бот инициирует DM сам: POST /api/v1/bot/dm с user_id целевого пользователя. Пользователь должен быть участником того же space'а. Endpoint идемпотентен — повторный вызов вернёт тот же conversation_id.

Как бот получает сообщения из DM#

Для групповых каналов real-time события доставляются через outgoing webhooks (HTTP POST на ваш URL). Для DM добавлен SSE Stream:

  • GET /api/v1/bot/stream — Server-Sent Events endpoint.
  • Auth: тот же Bearer token.
  • Каждое сообщение — data: <json>\n\n с событием message.new.
  • Payload сообщения совпадает с MESSAGE_CREATED в Poll/Webhook, плюс SSE-поле event. Внутри есть conversation_context: для DM это scope="dm" и is_direct=true.
  • Keep-alive :ping каждые 30 секунд.
  • Offline persistence: если бот не подключён, сообщения сохраняются во временный inbox (до 50, TTL 24ч). При следующем подключении backlog доставляется первым, затем поток переключается на live.

Как отличать DM, public/private и треды#

Во всех bot-facing событиях сообщений (MESSAGE_CREATED, MESSAGE_UPDATED, MESSAGE_DELETED, Bot Poll, Bot Webhook, outgoing webhook data и Bot DM SSE) есть единый объект conversation_context.

  • type — raw conversation type: chat, channel, voice, thread или будущее значение как есть.
  • scope — клиентский scope: dm, public, private.
  • Для тредов scope берётся от родительской беседы, а parent_conversation_id, parent_conversation_type, parent_is_public показывают родителя.
  • subtype="call_thread" ставится только для Call Thread гостевого voice-чата. В остальном такой чат остаётся обычным type="thread".

Старые flat-поля (conversation_id, thread_id, parent_message_id) остаются в payload для совместимости, но новый код должен использовать conversation_context как основной индикатор.

Почему разные механизмы для DM и каналов?#

  • Групповые каналы — много подписчиков, сложная routing-логика (filters, keywords, mentions), нужна надёжная доставка с retry. Outgoing webhook с HMAC и exponential backoff решает это.
  • DM — ровно один получатель (бот), низкая latency важнее сложной retry-логики. SSE даёт мгновенную доставку без необходимости публиковать HTTPS endpoint на стороне бота.

Ограничения#

  • SSE работает только для DM. Попытка использовать его для группового канала не имеет смысла — бот в групповом канале не получит туда сообщения через SSE.
  • Rate limit на POST /api/v1/bot/dm — 1 запрос/сек на бота.
  • Одно SSE-соединение на bot token. Новое соединение заменит предыдущее.

Custom commands и Interactive#

Бот может расширить функциональность канала двумя смежными фичами:

  • Custom slash commands — бот регистрирует свою команду (/deploy prod). Если бот работает в event_delivery_mode="poll", invocation приходит через GET /api/v1/bot/events; иначе PartyFlow шлёт HMAC-подписанный callback на URL команды. См. reference/slash-commands.md.
  • Interactive components — бот прикрепляет кнопки / select menus / модалки к сообщениям. При клике PartyFlow доставляет button.click / select.submit / modal.submit либо HMAC callback'ом, либо через Bot Poll API с interaction_id. См. reference/interactive-components.md.

В callback mode эти фичи используют отдельные signing secrets — не путать с Bearer token'ом бота. В poll mode отдельный callback secret для доставки не нужен: бот забирает события авторизованным Bearer token'ом и отвечает на interactive-события через POST /api/v1/bot/interactions/{interaction_id}/respond.


Best practices#

  1. Token hygiene. Один инсталляция бота — один токен. Не переиспользовать токен между dev/staging/prod. Если нужно — создавайте отдельных ботов (или используйте несколько токенов одного бота через Regenerate для blue/green).
  2. Idempotent retry. PartyFlow не дедуплицирует POST /api/v1/bot/messages — если вы ретраите 5xx, считайте, что сообщение может задвоиться. Делайте сам контент idempotent (включайте request_id в текст или проверяйте перед retry через Read API, что предыдущий POST не прошёл).
  3. Graceful 401. После Regenerate token старые токены мгновенно недействительны. Бот должен уметь прочитать свежий секрет из secret manager и перезапустить HTTP-клиент, а не крэшить в бесконечный 401-цикл.
  4. Handle 403 on join. Если send_message вернул 403, это значит бота выкинули из канала. Сначала POST /api/v1/bot/conversations/{id}/join и только потом retry send.
  5. Backoff at 429. Уважайте Retry-After. Для Bot Read API лимит 10 req/min/channel — если вы делаете summarize на большом канале, подождите окно вместо спама.
  6. Не полагайтесь только на HTTP 200 как на health-check. Сначала GET /api/v1/bot/me: 401 означает недействительный/перевыпущенный токен или отключённого бота, а при 200 проверяйте оба поля — ok: true и bot.is_active: true.
  7. Real-time для DM через SSE. Для direct-чатов бот может открыть GET /api/v1/bot/stream (SSE) и получать сообщения в реальном времени. Для групповых каналов real-time по-прежнему доступен только через outgoing webhooks — SSE работает исключительно в DM. См. reference/bot-rest-api.md → SSE Stream.
  8. Poll для серверов без публичного HTTPS. Если бот не может принять callback извне, включите event_delivery_mode="poll" и забирайте message, slash и interactive-события через GET /api/v1/bot/events.
  9. Для долгих AI-ответов используйте Agent Runs. Не отправляйте "думаю..." отдельными сообщениями: создайте один run, обновляйте sequence монотонно и завершайте completed с финальным public_text. Не передавайте raw reasoning, prompt'ы, token'ы, секреты или tool payloads.
  10. Для агентских задач используйте Execution Task widget. Создайте task, свяжите Agent Run через widget_id, а финальные файлы передавайте только как поле attachments в terminal update Agent Run после проверки PartyFlow.

Что дальше#