PartyFlow

Reference: Bot REST API#

REST API, которым бот отправляет сообщения в каналы/DM, загружает файлы, ведёт Agent Runs и создаёт Execution Tasks.


Base URL#

https://api.partyflow.ru

Адрес вашей инсталляции PartyFlow. Во всех примерах ниже используется $BASE как переменная.


Authentication#

Все запросы требуют Bearer token:

Authorization: Bearer fri_bot_<token>

Токен имеет формат fri_bot_<base64url>, генерируется при создании бота в UI (Integrations → Bots → Create) и показывается один раз. Сохраните сразу.

При утечке — Regenerate token в UI. Старый перестаёт работать мгновенно.


Карта endpoint'ов#

Сценарий Endpoint Что делает
Сообщения POST /api/v1/bot/messages Отправляет сообщение; top-level attachments принимает файлы из Bot Files API.
Сообщения PATCH /api/v1/bot/messages/{message_id} Редактирует только content и metadata_json сообщения этого бота. Файлы через этот endpoint не меняются.
Файлы POST /api/v1/bot/files/upload_sessions Создаёт short-lived upload URL и возвращает file_id.
Файлы POST /api/v1/bot/files/{file_id}/confirm Подтверждает успешную загрузку и переводит файл в ready.
Файлы GET /api/v1/bot/files/{file_id}/download_url?conversation_id={uuid} Возвращает short-lived URL для скачивания файла, доступного этому боту.
Execution Tasks POST /api/v1/bot/execution_tasks Создаёт task widget и одноразовый runtime_capability.token для сценариев, где runtime агента не хранит bot token.
Execution Tasks GET /api/v1/bot/execution_tasks/{widget_id} Возвращает public snapshot task.
Execution Tasks PATCH /api/v1/bot/execution_tasks/{widget_id} Обновляет task по sequence.
Execution Tasks POST /api/v1/bot/execution_tasks/{widget_id}/cancel Отменяет task.
Agent Runs POST /api/v1/bot/agent_runs Создаёт long-running ответ и placeholder-сообщение.
Agent Runs PATCH /api/v1/bot/agent_runs/{run_id} Обновляет статус; terminal completed может принять final attachments.
Agent Runs GET /api/v1/bot/agent_runs/{run_id} Возвращает текущее состояние run.
Agent Runs POST /api/v1/bot/agent_runs/{run_id}/cancel Отменяет run.
Бот и каналы GET /api/v1/bot/me Проверяет token и текущую конфигурацию бота.
Бот и каналы POST /api/v1/bot/conversations/{id}/join Добавляет бота в групповой канал.
Бот и каналы POST /api/v1/bot/dm Создаёт или возвращает DM с пользователем.
События POST /api/v1/bot/interactions/{interaction_id}/respond Отвечает на interactive event из Poll API.
События GET /api/v1/channels/{conversation_id}/messages Читает историю канала для контекста.
События GET /api/v1/bot/stream SSE stream сообщений из DM.

Endpoints#

POST /api/v1/bot/messages#

Отправить сообщение в канал, тред или уже созданный DM с ботом.

Request body:

{
  "conversation_id": "<uuid>",
  "content": "Hello from bot!",
  "display_name": "Standup Bot",
  "display_avatar_url": "https://cdn.example.com/bot-avatar.png",
  "parent_message_id": "<uuid>",
  "metadata_json": "{\"version\":1,\"blocks\":[...]}",
  "attachments": [{"file_id": "<uuid>"}]
}
Поле Тип Обязательно Описание
conversation_id string (uuid) Да ID канала, треда или DM, куда слать сообщение. DM можно получить через POST /api/v1/bot/dm или пользовательский direct-чат с ботом.
content string Да, если нет attachments Markdown-текст сообщения. До 4000 символов. Для отправки используйте именно content; поле text в этом endpoint'е не принимается.
display_name string Нет Переопределяет имя бота для этого сообщения.
display_avatar_url string Нет Переопределяет аватар бота для этого сообщения. HTTPS only.
parent_message_id string (uuid) Нет ID родительского/root-сообщения — ответ приходит в тред. thread_id временно принимается как legacy alias.
metadata_json string Нет JSON-строка с attachments/blocks/buttons — см. reference/message-format.md и reference/interactive-components.md. До 32 KiB. Для интерактивных блоков нужен callback URL или event_delivery_mode="poll".
attachments array Нет Файлы [{ "file_id": "..." }], загруженные и подтверждённые через Bot Files API, максимум 15. Нужен хотя бы content или один attachment. Это не Slack metadata_json.attachments.

Response (200):

{
  "ok": true,
  "message_id": "<uuid>",
  "attachment_status": "attached"
}

attachment_status появляется только для сообщений с файловыми attachments: attached означает, что PartyFlow уже связал файлы с сообщением; pending — сообщение создано, платформа продолжит best-effort attach retry; failed — файлы не удалось связать из-за постоянной ошибки.

Ошибки:

Status Причина
400 Невалидный JSON, отсутствует conversation_id, нет ни content ни attachments, content > 4000 символов, невалидный display_avatar_url, файл ещё не ready.
401 Невалидный или отозванный token.
403 Бот не состоит в канале или файл относится к другой conversation/space. Сначала вызовите /api/v1/bot/conversations/{id}/join.
404 Файл не найден для текущего bot token.
503 Платформа временно недоступна. Ретраить с backoff.
500 Внутренняя ошибка. Ретраить с backoff.

Rate limit: 10 msg/sec на канал.

Async slash response: если запрос содержит заголовок X-PartyFlow-Trigger-Id, PartyFlow считает его финальным ответом на custom slash command. В этом режиме body поддерживает content и optional metadata_json; conversation_id, parent_message_id, thread_id и bot_id нельзя переопределить body-полями, routing берётся из сохранённого trigger context. Trigger одноразовый и живёт 5 минут. Файловые attachments в async slash response не поддерживаются.


PATCH /api/v1/bot/messages/{message_id}#

Полностью заменить текст и metadata_json у сообщения, автором которого является этот же бот.

Request body:

{
  "content": "Updated text",
  "metadata_json": "{\"version\":1,\"blocks\":[...]}"
}
Поле Тип Обязательно Описание
content string Да Новый Markdown-текст, до 4000 символов.
metadata_json string Нет Новая metadata JSON-строка. Если поле не передано, metadata очищается.

Response (200):

{"ok": true, "message_id": "<uuid>"}

PATCH использует bot token и не позволяет редактировать чужие сообщения: платформа проверяет, что сообщение создано этим же ботом.

PATCH /api/v1/bot/messages/{message_id} не принимает файловые attachments. Чтобы добавить или заменить файлы, загрузите их через Bot Files API и отправьте новое POST /api/v1/bot/messages; для долгих AI-ответов final files передаются через terminal PATCH /api/v1/bot/agent_runs/{run_id}.


Bot Files API#

Файлы Bot REST загружаются через short-lived upload URL, без проксирования байтов через Bot REST API. Используется то же приватное хранилище, что и для обычных chat attachments.

Файл привязан к текущему bot token, space и conversation. Его можно прикрепить только к сообщению этого же бота в той же conversation.

Как загрузить файл и прикрепить к сообщению#

  1. Создайте upload session через POST /api/v1/bot/files/upload_sessions.
  2. Загрузите байты напрямую по upload.upload_url методом из upload.method и с заголовками из upload.headers.
  3. Подтвердите загрузку через POST /api/v1/bot/files/{file_id}/confirm.
  4. Передайте attachments: [{"file_id":"..."}] в POST /api/v1/bot/messages или в terminal PATCH /api/v1/bot/agent_runs/{run_id} при status="completed".

Минимальная последовательность:

curl -X POST "$BASE/api/v1/bot/files/upload_sessions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"conversation_id\":\"$CHANNEL_ID\",\"file_name\":\"report.pdf\",\"file_size\":1048576,\"mime_type\":\"application/pdf\"}"
 
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @report.pdf
 
curl -X POST "$BASE/api/v1/bot/files/$FILE_ID/confirm" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"conversation_id\":\"$CHANNEL_ID\"}"
 
curl -X POST "$BASE/api/v1/bot/messages" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"conversation_id\":\"$CHANNEL_ID\",\"content\":\"Отчёт готов\",\"attachments\":[{\"file_id\":\"$FILE_ID\"}]}"

$FILE_ID, $UPLOAD_URL, HTTP method и upload headers берите из ответа upload_sessions. До confirm файл имеет статус uploading и не может быть прикреплён к сообщению.

POST /api/v1/bot/files/upload_sessions#

Создать upload session.

{
  "conversation_id": "<uuid>",
  "file_name": "report.pdf",
  "file_size": 1048576,
  "mime_type": "application/pdf"
}

Response (200):

{
  "ok": true,
  "upload": {
    "file_id": "<uuid>",
    "upload_url": "https://storage.example/...",
    "method": "PUT",
    "headers": {"Content-Type": "application/pdf"},
    "expires_at": 1779523200
  },
  "file": {
    "file_id": "<uuid>",
    "conversation_id": "<uuid>",
    "message_id": "",
    "file_type": "document",
    "file_name": "report.pdf",
    "mime_type": "application/pdf",
    "file_size": 1048576,
    "status": "uploading",
    "created_at_unix": 1779522900,
    "width": 0,
    "height": 0
  }
}

Загрузите файл напрямую:

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @report.pdf

Ответ не содержит постоянных идентификаторов приватного хранилища или постоянный download URL.

POST /api/v1/bot/files/{file_id}/confirm#

Подтвердить upload после успешного PUT.

{"conversation_id": "<uuid>"}

PartyFlow проверит, что файл появился в приватном хранилище, сверит фактический размер с лимитами и для generic application/octet-stream может уточнить file type по magic bytes. После подтверждения файл получает status="ready" и может быть передан в POST /api/v1/bot/messages.attachments или terminal PATCH /api/v1/bot/agent_runs/{run_id}.

Response (200):

{"ok": true, "file": {"file_id": "<uuid>", "status": "ready"}}

GET /api/v1/bot/files/{file_id}/download_url?conversation_id={uuid}#

Вернуть short-lived URL для скачивания файла, доступного текущему боту.

Response (200):

{
  "ok": true,
  "file_id": "<uuid>",
  "download_url": "https://storage.example/...",
  "expires_at": 1779523500,
  "file": {"file_id": "<uuid>", "status": "ready"}
}

Ошибки: 400 — невалидный body/размер/MIME или файл ещё не ready; 401 — невалидный token; 403 — бот не состоит в conversation или файл из другого space/conversation; 404 — файл не найден для текущего token; 503 — платформа временно недоступна. Тело ошибки сейчас имеет форму {"error":"..."} без стабильного machine-readable code.

Rate limit: Bot Files endpoints используют тот же per-bot×conversation fixed-window лимит, что и Bot REST writes. При превышении PartyFlow отвечает 429 + Retry-After.


Execution Tasks: widget-backed task UI#

Execution Task — это управляемый task widget в чате. Бот создаёт task, обновляет публичный status/title/description и может связать task с Agent Run. Когда связанный Agent Run переходит в terminal status, PartyFlow обновляет task и прикрепляет только проверенные финальные файлы.

space_id, bot_id и actor нельзя передать в body: они всегда выводятся из Bearer bot token.

Типовой flow для AI agent#

  1. Получите пользовательский запрос через outgoing webhook, Poll API или SSE Stream для DM.
  2. Если работа должна быть видимой как отдельная задача, создайте execution_task через POST /api/v1/bot/execution_tasks. Сохраните widget_id и execution_task.sequence.
  3. Создайте Agent Run через POST /api/v1/bot/agent_runs и передайте этот widget_id. Виджет должен быть создан тем же bot actor в той же conversation; произвольный чужой widget_id будет отклонён.
  4. Пока агент работает, обновляйте Agent Run статусами gathering_context, using_tool, finalizing и т.п. Если нужно отдельно поменять task UI, вызывайте PATCH /api/v1/bot/execution_tasks/{widget_id} с текущим execution_task.sequence.
  5. Финальные файлы сначала загрузите через Bot Files API, затем передайте их в terminal PATCH /api/v1/bot/agent_runs/{run_id}. PartyFlow перенесёт только проверенные ссылки на финальные файлы в task widget.
  6. Человеческий reviewer принимает или отклоняет результат в widget UI. Bot REST v1 не подменяет reviewer и не должен отправлять prompts, raw reasoning, tool payloads, stack traces или секреты в public fields.

agent_run.sequence и execution_task.sequence — разные версии. Для Agent Run увеличивайте run sequence монотонно. Для task PATCH используйте последний execution_task.sequence, полученный из create/get/patch ответа.

POST /api/v1/bot/execution_tasks#

Создать execution task. Запрос идемпотентен по Idempotency-Key header или body-полю idempotency_key.

{
  "idempotency_key": "build-124:<message_uuid>",
  "conversation_id": "<uuid>",
  "parent_message_id": "<uuid>",
  "agent_run_id": "<uuid>",
  "title": "Проверить failed build",
  "description": "CI вернул ошибку на main",
  "allowed_actions": ["update", "cancel"]
}

Для source по parent_message_id поле conversation_id обязательно: root message должен существовать в этой беседе. Можно передать thread_id вместо parent_message_id. Если передан только conversation_id, PartyFlow создаст silent bot-сообщение-якорь в этой беседе и привяжет widget к thread от этого сообщения.

Response (200):

{
  "ok": true,
  "execution_task": {
    "widget_id": "<uuid>",
    "conversation_id": "<uuid>",
    "agent_run_id": "<uuid>",
    "status": "open",
    "title": "Проверить failed build",
    "description": "CI вернул ошибку на main",
    "public_text": "",
    "error_code": "",
    "sequence": 1,
    "artifacts": [],
    "created_at": "2026-05-24T10:00:00Z",
    "updated_at": "2026-05-24T10:00:00Z",
    "closed_at": null
  },
  "runtime_capability": {
    "token": "fri_exec_...",
    "token_prefix": "fri_exec_abcd1234",
    "allowed_actions": ["update", "cancel"],
    "expires_at": "2026-05-24T11:00:00Z"
  }
}

runtime_capability.token показывается один раз. Он scoped к текущему space_id, widget_id, optional agent_run_id, allowed actions и expiry. В текущем Bot REST v1 все endpoints выше аутентифицируются Bearer bot token: не передавайте fri_exec_ token вместо fri_bot_. Capability возвращается для сценариев, где runtime агента не хранит bot token, и будет использоваться только в endpoints, которые явно документируют auth через fri_exec_.

GET /api/v1/bot/execution_tasks/{widget_id}#

Вернуть safe public snapshot task. Bot token видит только task'и своего space/bot. В ответе могут прийти статусы open, in_progress, blocked, completed, accepted, rejected, cancelled или closed.

PATCH /api/v1/bot/execution_tasks/{widget_id}#

Применить действие к task. sequence обязателен и должен равняться текущему execution_task.sequence.

{
  "action": "update",
  "status": "in_progress",
  "sequence": 2,
  "title": "Проверить failed build",
  "description": "Checks passed",
  "public_text": "Deploy started",
  "idempotency_key": "build-124:<message_uuid>:2"
}

Поддерживаемые v1 actions: update, complete, fail, cancel. Для update используйте task statuses open, in_progress, blocked или completed. Action complete переводит task в completed, fail — в blocked, cancel — в cancelled.

POST /api/v1/bot/execution_tasks/{widget_id}/cancel#

Закрыть task как cancelled. Endpoint не принимает body; если вам нужен optimistic-lock через sequence, используйте PATCH с action cancel.

Ошибки: 400 — невалидный JSON/UUID/title/idempotency/action/status; 401 — невалидный token; 403 — bot не имеет доступа к task/conversation; 404 — task не найден; 409 — stale sequence или terminal state; 429 — rate limit; 503 — нужные возможности временно недоступны на инстансе.


Agent Runs: responsive AI replies#

Agent Run помогает долгим AI-ботам не оставлять пользователя в тишине. Бот создаёт placeholder-сообщение, а затем обновляет его безопасными статусами. Пользователи видят обычные события сообщения: сначала message.new, затем message.edited. Отдельного stream/API agent.run.* нет.

POST /api/v1/bot/agent_runs#

Создать run и placeholder-сообщение.

Headers:

Idempotency-Key: <stable-key-per-user-request>

Idempotency-Key обязателен. Повтор POST с тем же ключом вернёт уже созданный run и не создаст второй placeholder. Ключ уникален в рамках текущего bot token; допустимы A-Z a-z 0-9 . _ : -, максимум 128 символов. Если header неудобен, можно передать тот же ключ в body-поле idempotency_key; если указаны оба источника, значения должны совпадать.

Request body:

{
  "idempotency_key": "user-message-<uuid>",
  "conversation_id": "<uuid>",
  "trigger_message_id": "<uuid>",
  "widget_id": "<uuid>",
  "parent_message_id": "<uuid>",
  "content": "Запрос принят"
}
Поле Тип Обязательно Описание
idempotency_key string Нет, если есть header Body fallback для обязательного Idempotency-Key.
conversation_id string (uuid) Да Канал, тред или DM, куда отправить placeholder.
trigger_message_id string (uuid) Да Сообщение пользователя, на которое отвечает агент. Должно существовать в том же space и conversation.
widget_id string (uuid) Нет Existing Execution Task widget, созданный тем же bot actor в этой conversation. Он получит terminal update этого run.
parent_message_id string (uuid) Нет Root-сообщение для thread reply. Должно существовать в том же space и conversation. thread_id временно принимается как legacy alias.
content string Нет Видимый fallback-текст placeholder, до 4000 символов. Если не передан, PartyFlow покажет текст статуса.

space_id, bot_id, status_text и bot_metadata_json нельзя передать в body. Они выводятся из bot token и разрешённого сервером списка.

Response (200):

{
  "ok": true,
  "agent_run": {
    "id": "<uuid>",
    "idempotency_key": "user-message-<uuid>",
    "conversation_id": "<uuid>",
    "trigger_message_id": "<uuid>",
    "message_id": "<uuid>",
    "widget_id": "<uuid>",
    "status": "accepted",
    "status_text": "Запрос принят",
    "sequence": 1,
    "is_terminal": false,
    "public_text": "Запрос принят",
    "error_code": "",
    "bot_metadata_json": "{\"version\":1,\"kind\":\"agent_run\",\"agent_run\":{\"run_id\":\"<uuid>\",\"status\":\"accepted\",\"status_text\":\"Запрос принят\",\"sequence\":1,\"is_terminal\":false,\"trigger_message_id\":\"<uuid>\",\"widget_id\":\"<uuid>\",\"updated_at\":\"2026-05-20T10:00:00Z\",\"completed_at\":null,\"error_code\":\"\"}}",
    "attachments": [],
    "attachment_status": "none",
    "started_at": "2026-05-20T10:00:00Z",
    "updated_at": "2026-05-20T10:00:00Z",
    "completed_at": null
  }
}

PATCH /api/v1/bot/agent_runs/{run_id}#

Обновить статус. Non-terminal статусы могут доставляться с короткой задержкой, terminal статусы приоритизируются. Клиентам следует считать источником истины последнее message.edited.

{
  "status": "completed",
  "sequence": 8,
  "public_text": "Готово, отчёт во вложении.",
  "error_code": "",
  "attachments": [{"file_id": "<uuid>"}]
}
Поле Тип Обязательно Описание
status string Да Один из публичных статусов ниже.
sequence int64 Да Монотонная версия от бота. Повтор того же sequence идемпотентен только с тем же payload; меньший или конфликтующий sequence вернёт 409.
public_text string Нет Видимый текст сообщения, до 4000 символов. Для completed обязателен. content принимается как alias.
error_code string Нет Безопасный код ошибки для failed: только буквы, цифры и _, до 64 символов.
attachments array Нет Финальные файлы [{ "file_id": "..." }], максимум 15. Разрешены только при status="completed"; файлы должны быть загружены через Bot Files API, иметь status="ready" и принадлежать тому же боту/conversation.

Response (200): тот же объект agent_run, что и в POST.

Идемпотентность для одинакового sequence работает только если нормализованные status / public_text / error_code полностью совпадают с уже сохранённым состоянием. Такой повтор вернёт текущий объект. Тот же sequence с другим payload считается stale update и вернёт 409.

Нельзя отправлять raw reasoning, prompt, token, tool payload, stack trace или секреты. В публичном UI показываются только public_text, server-side status_text и безопасный error_code.

Status status_text Terminal
accepted Запрос принят no
queued В очереди no
gathering_context Ищу контекст no
using_tool Использую инструмент no
waiting_tool Жду ответ инструмента no
waiting_user Жду ответа пользователя no
finalizing Готовлю финальный ответ no
completed Готово yes
failed Не удалось выполнить yes
cancelled Остановлено yes
expired Остановлено по тайм-ауту yes

GET /api/v1/bot/agent_runs/{run_id}#

Вернуть текущее состояние run. Bot token видит только свои run'ы.

Response (200): тот же объект agent_run, что и в POST.

POST /api/v1/bot/agent_runs/{run_id}/cancel#

Отменить run от имени bot runtime. Это не пользовательский cancel UI; он может быть добавлен позже поверх того же статуса cancelled.

Response (200): тот же объект agent_run, что и в POST.

Поля объекта agent_run:

Поле Null/omitted Описание
idempotency_key всегда присутствует Ключ create-запроса. Нужен bot runtime'у для диагностики retry.
parent_message_id отсутствует, если run не является reply/thread reply Root-сообщение, к которому привязан placeholder.
trigger_message_id присутствует после успешного create Сообщение пользователя, запустившее run.
message_id присутствует после успешного create Placeholder bot message, который будет редактироваться статусами.
widget_id отсутствует, если run не связан с execution task Task widget, который получает terminal update с проверенными финальными файлами.
attachments всегда массив Финальные file_id, переданные при status="completed".
attachment_status всегда присутствует none, pending, attached или failed.
completed_at null до terminal статуса Время перехода в completed / failed / cancelled / expired.

Rate limits: create ограничен per bot + conversation, update/cancel — per bot + run, дополнительно есть global per-bot write cap. При превышении PartyFlow вернёт 429 и Retry-After.

Ошибки: 400 — невалидный body/UUID/status/sequence/content/idempotency key; 401 — невалидный token; 403 — бот не состоит в conversation; 404 — run не найден для этого бота; 409 — terminal run, stale sequence или повторное использование Idempotency-Key для другого conversation/trigger; 429 — rate limit; 503 — временная недоступность платформы или rate limiter.


GET /api/v1/bot/me#

Информация о текущем боте. Полезно для проверки, что токен жив, и для отображения имени/аватара в вашей UI.

Response (200):

{
  "ok": true,
  "bot": {
    "id": "<uuid>",
    "space_id": "<uuid>",
    "display_name": "Standup Bot",
    "avatar_url": "https://cdn.example.com/bot-avatar.png",
    "is_active": true,
    "event_types": ["MESSAGE_CREATED", "REACTION_ADDED"],
    "channel_ids": ["<uuid>"],
    "event_delivery_mode": "poll",
    "webhook_url": ""
  }
}
Поле Тип Описание
id string (uuid) ID бота.
space_id string (uuid) Space, которому принадлежит бот.
display_name string Имя бота для отображения.
avatar_url string URL аватара; может быть пустой строкой.
is_active bool Активен ли бот.
event_types string[] События, на которые подписан бот (MESSAGE_CREATED, REACTION_ADDED, ...).
channel_ids string[] Каналы, где бот получает события. Пустой массив означает все каналы space'а.
event_delivery_mode string "none", "poll" или "webhook".
webhook_url string URL push webhook'а при event_delivery_mode="webhook"; пустая строка иначе. webhook_signing_secret здесь не возвращается.

Если бот отключён админом или token revoked, Bot REST запросы обычно возвращают HTTP 401. Поле is_active в успешном ответе помогает явно проверить статус текущего бота.


POST /api/v1/bot/conversations/{id}/join#

Добавить бота как участника канала. Бот появится в списке участников, сможет отправлять сообщения.

Идемпотентно: повторный вызов для уже добавленного бота — no-op, возвращает HTTP 200.

Проверки:

  • Бот должен принадлежать тому же space, что и канал.
  • Канал должен существовать.
  • Для /join поддерживаются только групповые каналы. DM создаются через POST /api/v1/bot/dm или пользователем в UI и не требуют join.

Response (200):

{"ok": true}

Ошибки:

Status Причина
400 Канал не найден, space mismatch, неподдерживаемый тип канала.
401 Невалидный/отозванный token.
503 Платформа временно недоступна. Ретраить с backoff.

POST /api/v1/bot/interactions/{interaction_id}/respond#

Асинхронный ответ на interactive-событие, которое poll-бот получил через GET /api/v1/bot/events. Синтаксис body такой же, как у sync callback:

{"update_message": {"text": "Approved", "metadata_json": ""}}
{"send_message": {"text": "Deploy started", "metadata_json": ""}}
{"ephemeral_text": "Действие принято."}
{"open_modal": {"title": "Next step", "fields": []}}
{"noop": true}

Response (200):

{"ok": true, "outcome": "update_message"}

interaction_id одноразовый. По умолчанию он живёт 15 минут. Для цепочек модалок можно вернуть open_modal после modal.submit; глубина ограничена настройкой инстанса.


4. Чтение истории канала#

GET /api/v1/channels/{conversation_id}/messages

Возвращает окно истории канала — для AI-ботов, которым нужен context (/summarize, long-memory, indexing). Бот должен быть участником канала — иначе 403.

Query параметры:

Параметр Тип По умолчанию Описание
limit int 1..100 50 Макс. количество сообщений в ответе.
before_msg_index int64 ≥ 0 0 (новейшие) Курсор пагинации в прошлое. Передавайте next_msg_index из предыдущего ответа, чтобы листать вглубь истории.
after_msg_index int64 ≥ 0 Курсор пагинации в будущее: "дай мне сообщения после этой точки". Используется для catch-up после реконнекта.
around_msg_index int64 ≥ 0 Окно вокруг конкретного сообщения — N сообщений до и после. Полезно когда outgoing webhook триггернул бота и ему нужен контекст триггерующего сообщения.
updated_since ISO 8601 Delta-sync: вернёт сообщения с modified_at > T. Индексер-боты используют для эффективной синхронизации без ре-пагинации всей истории.
thread_id string Если указан, возвращаются только сообщения треда (root + replies). Фильтр делается chat-side — над-выборка не шлётся в ответе.

Ограничения: только один из before_msg_index, after_msg_index, around_msg_index можно передать в одном запросе. Если указано больше одного — ответ 400.

Успешный ответ 200:

{
  "messages": [
    {
      "id": "msg_uuid",
      "text": "Hello",
      "author_id": "user_uuid",
      "author_name": "Alice",
      "author_type": "user",
      "author_subtype": "",
      "created_at": "2026-04-17T10:00:00.000Z",
      "modified_at": "2026-04-17T10:00:00.000Z",
      "msg_index": 42,
      "parent_message_id": null,
      "is_deleted": false,
      "is_system": false,
      "system_type": null,
      "system_data": null,
      "reaction_count": 0,
      "attachments": [],
      "bot_metadata_json": null,
      "mentions": []
    }
  ],
  "has_more": true,
  "next_msg_index": 23
}

Сортировка — новейшие первые. Для pagination передавайте next_msg_index последнего ответа как before_msg_index следующего запроса; когда has_more=false — история исчерпана.

System-события (is_system: true) — joins/leaves/renames. Ценны для AI summarizers:

{
  "id": "sys-msg",
  "is_system": true,
  "system_type": "user_joined",
  "system_data": {
    "user_id": "user-42",
    "user_name": "Alice",
    "target_id": "conv-1",
    "target_name": "#general",
    "old_value": null,
    "new_value": null
  },
  "text": "",
  "author_id": "",
  "msg_index": 5
}

Привилегии и приватность:

  • read_receipts, edit_history, delivery_status, parent_preview, mention_details НЕ возвращаются ботам (privacy).
  • system_data.extra_json скрыто — поле может содержать device/IP; если нужно поле из extra_json, запросите добавление typed атрибута через platform team.
  • Для is_deleted=true поля text обнуляются (tombstone), attachments всегда []; id/author/timestamps сохраняются для audit.
  • mentions — только user_id упомянутых пользователей, без display names (для имён используйте author_name того, кто @упомянут — но только если он автор самого сообщения).
  • attachments и mentions всегда массивы, никогда null — JSON-schema клиенты итерируют без nil-checks.

Rate limits:

Scope Лимит
Per bot × channel 10 запросов / минуту
Per bot (все каналы) 100 запросов / минуту

Превышение любого лимита → HTTP 429 + заголовок Retry-After: <секунд-до-окна>.

Ошибки:

Status Причина
400 Некорректный limit/before_msg_index/path.
401 Невалидный/отозванный token.
403 Бот не участник канала ИЛИ канал в чужом space.
429 Rate limit. Читайте Retry-After и повторите после ожидания.
502 Платформа временно недоступна.

Пример:

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/v1/channels/$CHANNEL_ID/messages?limit=50&thread_id=$ROOT_MSG_ID"

Типовой сценарий#

BASE='https://api.partyflow.ru'
TOKEN='fri_bot_...'
CHANNEL_ID='<uuid-канала>'
 
# 1. Проверить, что бот жив
curl "$BASE/api/v1/bot/me" \
  -H "Authorization: Bearer $TOKEN"
 
# 2. Вступить в канал (идемпотентно)
curl -X POST "$BASE/api/v1/bot/conversations/$CHANNEL_ID/join" \
  -H "Authorization: Bearer $TOKEN"
 
# 3. Отправить сообщение
curl -X POST "$BASE/api/v1/bot/messages" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"conversation_id\":\"$CHANNEL_ID\",\"content\":\"Good morning, team!\"}"

Python#

import requests
 
BASE = "https://api.partyflow.ru"
TOKEN = "fri_bot_..."
 
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {TOKEN}"})
 
# Self-check
me = session.get(f"{BASE}/api/v1/bot/me").json()
print(f"Bot: {me['bot']['display_name']}, active: {me['bot']['is_active']}")
 
# Join channel (idempotent)
channel_id = "..."
session.post(f"{BASE}/api/v1/bot/conversations/{channel_id}/join")
 
# Send message
resp = session.post(
    f"{BASE}/api/v1/bot/messages",
    json={"conversation_id": channel_id, "content": "Hello from Python bot!"},
)
resp.raise_for_status()
print(resp.json())

Node.js#

const BASE = "https://api.partyflow.ru";
const TOKEN = "fri_bot_...";
 
const headers = { "Authorization": `Bearer ${TOKEN}`, "Content-Type": "application/json" };
 
// Self-check
const me = await fetch(`${BASE}/api/v1/bot/me`, { headers }).then(r => r.json());
console.log(`Bot: ${me.bot.display_name}, active: ${me.bot.is_active}`);
 
// Join
const channelId = "...";
await fetch(`${BASE}/api/v1/bot/conversations/${channelId}/join`, { method: "POST", headers });
 
// Send
const resp = await fetch(`${BASE}/api/v1/bot/messages`, {
  method: "POST",
  headers,
  body: JSON.stringify({ conversation_id: channelId, content: "Hello from Node bot!" }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());

5. Создать DM с пользователем#

POST /api/v1/bot/dm

Бот инициирует direct-чат с пользователем. Если DM уже существует — возвращается HTTP 200 с ID существующего conversation.

Request body:

{
  "user_id": "<uuid-пользователя>"
}
Поле Тип Обязательно Описание
user_id string (uuid) Да ID пользователя, с которым начать личку. Пользователь должен быть участником того же space'а.

Response (200):

{
  "ok": true,
  "conversation_id": "<uuid>"
}

Ошибки:

Status Причина
400 Отсутствует user_id, пользователь не найден в space'е, user_id == bot_id.
401 Невалидный/отозванный token.
429 Превышен rate limit — 1 запрос/сек на бота.

6. SSE Stream — real-time сообщения из DM#

GET /api/v1/bot/stream

Server-Sent Events endpoint для получения сообщений из direct-чатов бота в реальном времени.

Auth: Bearer token в заголовке Authorization (как для всех bot endpoints).

Headers ответа:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

Формат сообщений:

data: {"event":"message.new","message_id":"...","conversation_id":"...","author_id":"...","text":"...","msg_index":1042,"sent_at":"2026-05-02T12:00:00Z","conversation_context":{"conversation_id":"...","type":"chat","scope":"dm","is_direct":true,"is_public":false}}
 

Каждое сообщение завершается двумя \n. Поле data — JSON-строка.

Автор сообщения передаётся плоским полем author_id, как в Bot Event Polling API и outgoing webhook data. Объект author / author.user_id в SSE payload не возвращается.

conversation_context совпадает с payload у MESSAGE_CREATED в Bot Event Polling, Bot Webhook и outgoing webhook data. Для DM scope="dm" и is_direct=true; для публичных/приватных каналов scope будет public или private. Для тредов type="thread", а scope наследуется от родительской беседы; Call Thread из гостевого voice-чата дополнительно получает subtype="call_thread".

Keep-alive: если в течение 30 секунд нет сообщений, сервер шлёт:

:ping
 

Это пустой comment в SSE-спецификации; клиент должен игнорировать его, но использовать для определения «соединение живо».

Offline backlog: если бот был offline, сообщения накапливались в inbox (до 50 штук, TTL 24ч). При подключении backlog доставляется первым в том же SSE-потоке, затем сервер переключается на live-режим.

Пример на JavaScript (EventSource не поддерживает custom headers — используйте fetch):

const TOKEN = "fri_bot_...";
const BASE = "https://api.partyflow.ru";
 
const response = await fetch(`${BASE}/api/v1/bot/stream`, {
  headers: { "Authorization": `Bearer ${TOKEN}` },
});
 
const reader = response.body.getReader();
const decoder = new TextDecoder();
 
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const chunk = decoder.decode(value, { stream: true });
  // Парсить SSE-фреймы: разделить по "\n\n", для каждого фрейма:
  //   - игнорировать если начинается с ":"
  //   - иначе убрать префикс "data: ", распарсить JSON
  console.log(chunk);
}

Обрыв соединения: рекомендуется reconnect с exponential backoff (начиная с 1с, max 30с). При reconnect backlog доставляется заново только если сообщения не были получены ранее — сервер atomically читает и удаляет inbox.

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

  • SSE работает только для DM. Сообщения из групповых каналов бот получает через outgoing webhooks, как раньше.
  • Нельзя открывать более одного SSE-соединения на один bot token одновременно: новое соединение заменит предыдущее.

Связанное#