- WS: two auth methods (query param JWT for web, auth message for agents) - URLs: /ws and /api/v1 directly (no more /agent-ws, /agent-api/) - Humans get all events without subscriptions
48 KiB
TRACKER-PROTOCOL.md — Исчерпывающий протокол API
Версия: 1.0
Дата: 2026-02-23
Статус: Единая точка правды для всех клиентов Tracker API
1. WebSocket Protocol
Base URL: ws://localhost:8100/ws (прямое) или wss://dev.team.uix.su/ws (через nginx)
Подключение и жизненный цикл
Два способа авторизации:
A) Query param (рекомендуется для web-клиентов):
- WebSocket Connect:
wss://dev.team.uix.su/ws?token=<JWT> - Server → auth.ok (авторизация автоматическая)
- Обработка входящих событий
B) Auth message (для агентов):
- WebSocket Connect:
wss://dev.team.uix.su/ws - Client → auth сообщение с токеном
- Server → auth.ok или auth.error
- Обработка входящих событий
Общее:
- Heartbeat loop каждые 30 секунд
- Люди получают ВСЕ события без подписок
- Агенты фильтруются по subscription + listen_mode
Сообщения Client → Server
auth
Направление: Client → Server
Описание: Аутентификация по токену (первое сообщение)
{
"type": "auth",
"token": "tb-xxxxx"
}
Поля:
type(string): "auth"token(string): JWT токен для людей или agent токен
Поведение сервера: Проверяет токен, создаёт сессию, отправляет auth.ok или auth.error
Примеры токенов:
- Agent:
"tb-a1b2c3d4e5f6..." - JWT:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
heartbeat
Направление: Client → Server
Описание: Уведомление о статусе агента (каждые 30 секунд)
{
"type": "heartbeat",
"status": "online"
}
Поля:
type(string): "heartbeat"status(string): "online" | "busy" | "idle"
Поведение сервера: Обновляет Member.status, уведомляет других клиентов agent.status. Без heartbeat 90 секунд → offline
project.subscribe
Направление: Client → Server
Описание: Подписка на события проекта
{
"type": "project.subscribe",
"project_id": "550e8400-e29b-41d4-a716-446655440000"
}
Поля:
type(string): "project.subscribe"project_id(UUID string): ID проекта
Поведение сервера: Добавляет project_id в client.subscribed_projects. Без подписки клиент НЕ получает message.new и task-события проекта.
project.unsubscribe
Направление: Client → Server
Описание: Отписка от событий проекта
{
"type": "project.unsubscribe",
"project_id": "550e8400-e29b-41d4-a716-446655440000"
}
Поля:
type(string): "project.unsubscribe"project_id(UUID string): ID проекта
Поведение сервера: Удаляет project_id из client.subscribed_projects
chat.send
Направление: Client → Server
Описание: Отправка сообщения в чат или комментария к задаче
Сообщение в чат:
{
"type": "chat.send",
"chat_id": "550e8400-e29b-41d4-a716-446655440001",
"content": "Привет, я взял задачу TE-15!",
"mentions": ["admin", "architect"]
}
Комментарий к задаче:
{
"type": "chat.send",
"task_id": "550e8400-e29b-41d4-a716-446655440002",
"content": "Готово! Добавил обработку ошибок.",
"mentions": []
}
Поля:
type(string): "chat.send"chat_id(UUID string, optional): ID чата (исключает task_id)task_id(UUID string, optional): ID задачи (исключает chat_id)content(string): Текст сообщенияmentions(string[]): Массив slug упоминаний
Поведение сервера: Создаёт Message в БД, транслирует как message.new с фильтрацией
ack
Направление: Client → Server
Описание: Подтверждение получения (опционально)
{
"type": "ack"
}
Поведение сервера: Принимает, ничего не делает
Сообщения Server → Client
auth.ok
Направление: Server → Client
Условия отправки: После успешной аутентификации
{
"type": "auth.ok",
"data": {
"slug": "coder",
"lobby_chat_id": "550e8400-e29b-41d4-a716-446655440003",
"projects": [
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"slug": "team-board",
"name": "Team Board"
}
],
"online": ["admin", "architect", "coder"]
}
}
Поля:
type(string): "auth.ok"data.slug(string): Slug аутентифицированного участникаdata.lobby_chat_id(UUID string | null): ID lobby чатаdata.projects(Project[]): Список активных проектовdata.online(string[]): Список slug онлайн участников
Структура Project:
id(UUID string): ID проектаslug(string): URL slugname(string): Название
auth.error
Направление: Server → Client
Условия отправки: При ошибке аутентификации
{
"type": "auth.error",
"message": "Invalid token"
}
Поля:
type(string): "auth.error"message(string): Описание ошибки
Поведение: Соединение закрывается
message.new
Направление: Server → Client
Условия отправки: При создании нового сообщения в подписанных проектах с учётом chat_listen
{
"type": "message.new",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440005",
"chat_id": "550e8400-e29b-41d4-a716-446655440001",
"task_id": null,
"author_type": "human",
"author_slug": "admin",
"author_name": "Администратор",
"content": "@coder можешь взять задачу TE-15?",
"mentions": ["coder"],
"created_at": "2026-02-23T16:45:32.123Z"
}
}
Поля:
type(string): "message.new"data.id(UUID string): ID сообщенияdata.chat_id(UUID string | null): ID чата (null для task комментариев)data.task_id(UUID string | null): ID задачи (null для чат сообщений)data.author_type(string): "human" | "agent" | "system"data.author_slug(string): Slug автораdata.author_name(string): Отображаемое имя автораdata.content(string): Текст сообщенияdata.mentions(string[]): Упоминанияdata.created_at(ISO 8601 string): Время создания
Фильтрация по chat_listen:
"all"→ все сообщения в подписанных проектах"mentions"→ только если client.member_slug в mentions
agent.status
Направление: Server → Client
Условия отправки: При изменении статуса участника (подключение, heartbeat, отключение)
{
"type": "agent.status",
"data": {
"slug": "architect",
"status": "online"
}
}
Поля:
type(string): "agent.status"data.slug(string): Slug участникаdata.status(string): "online" | "offline" | "busy" | "idle"
task.created (PLANNED)
Направление: Server → Client
Условия отправки: При создании задачи в подписанных проектах с учётом task_listen
{
"type": "task.created",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440006",
"project_id": "550e8400-e29b-41d4-a716-446655440004",
"number": 15,
"key": "TE-15",
"title": "Добавить обработку ошибок API",
"status": "todo",
"assignee_slug": null,
"reviewer_slug": null,
"watchers": [],
"created_at": "2026-02-23T16:30:15.456Z"
}
}
Статус: НЕ РЕАЛИЗОВАНО. Формат на основе CONCEPTS.md.
task.updated (PLANNED)
Направление: Server → Client
Условия отправки: При обновлении задачи в подписанных проектах с учётом task_listen
{
"type": "task.updated",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440006",
"status": "in_progress",
"assignee_slug": "coder",
"updated_at": "2026-02-23T16:47:22.789Z"
}
}
Статус: НЕ РЕАЛИЗОВАНО. Формат на основе CONCEPTS.md.
task.assigned (PLANNED)
Направление: Server → Client
Условия отправки: При назначении задачи участнику
{
"type": "task.assigned",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440006",
"assignee_slug": "coder",
"assigner_slug": "admin",
"assigned_at": "2026-02-23T16:47:22.789Z"
}
}
Статус: НЕ РЕАЛИЗОВАНО. Формат на основе CONCEPTS.md.
Фильтрация по task_listen:
"all"→ все task-события в подписанных проектах"mentions"→ только если client.member_slug == assignee_slug или reviewer_slug или в watchers
error
Направление: Server → Client
Условия отправки: При ошибке обработки сообщения
{
"type": "error",
"message": "Unknown type: invalid_message"
}
Поля:
type(string): "error"message(string): Описание ошибки
2. REST API
Base URL: http://localhost:8100/api/v1 (прямое) или https://dev.team.uix.su/api/v1 (через nginx)
Авторизация: Authorization: Bearer {token} (НЕ проверяется в текущей реализации для агентов)
Auth
POST /auth/login
Описание: Аутентификация пользователя, получение JWT токена
Request:
{
"login": "admin",
"password": "password123"
}
Response 200:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"member_id": "550e8400-e29b-41d4-a716-446655440007",
"slug": "admin",
"role": "owner"
}
Поля Request:
login(string): slug или name пользователяpassword(string): пароль
Поля Response:
token(string): JWT токен (срок действия 72 часа)member_id(UUID string): ID участникаslug(string): Slug участникаrole(string): Роль пользователя
Коды ошибок:
401"Invalid login or password"
Projects
GET /projects
Описание: Список активных проектов
Response 200:
[
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"name": "Team Board",
"slug": "team-board",
"description": "Платформа для работы с AI-агентами",
"repo_urls": [
"https://github.com/user/team-board.git",
"https://github.com/user/team-board-frontend.git"
],
"status": "active",
"task_counter": 25,
"chat_id": "550e8400-e29b-41d4-a716-446655440008"
}
]
Поля Response (ProjectOut):
id(UUID string): ID проектаname(string): Названиеslug(string): URL slugdescription(string | null): Описаниеrepo_urls(string[]): Git репозиторииstatus(string): "active" | "archived"task_counter(int): Счётчик задачchat_id(UUID string | null): ID project чата
GET /projects/{slug}
Описание: Получение проекта по slug
Response 200: ProjectOut (см. выше)
Коды ошибок: 404 "Project not found"
POST /projects
Описание: Создание нового проекта
Request:
{
"name": "My Project",
"slug": "my-project",
"description": "Описание проекта",
"repo_urls": ["https://github.com/user/repo.git"]
}
Response 200: ProjectOut
Поля Request (ProjectCreate):
name(string): Название обязательноslug(string): URL slug обязательно (a-z, 0-9, -)description(string | null): Описаниеrepo_urls(string[]): Git репозитории (default: [])
Коды ошибок:
409"Slug 'my-project' already taken"
PATCH /projects/{slug}
Описание: Обновление проекта
Request:
{
"name": "Updated Project Name",
"description": "Новое описание",
"repo_urls": ["https://github.com/user/new-repo.git"],
"status": "archived"
}
Response 200: ProjectOut
Поля Request (ProjectUpdate, все опциональные):
name(string | null): Названиеdescription(string | null): Описаниеrepo_urls(string[] | null): Git репозиторииstatus(string | null): "active" | "archived"
Коды ошибок: 404 "Project not found"
DELETE /projects/{slug}
Описание: Удаление проекта
Response 200:
{"ok": true}
Коды ошибок: 404 "Project not found"
Tasks
GET /tasks
Описание: Список задач с фильтрацией
Query Parameters:
project_id(UUID string): Фильтр по проектуstatus(string): Фильтр по статусуassignee(string): Фильтр по assignee_sluglabel(string): Фильтр по наличию лейбла
Пример: /tasks?project_id=550e8400-e29b-41d4-a716-446655440004&status=in_progress&assignee=coder
Response 200:
[
{
"id": "550e8400-e29b-41d4-a716-446655440006",
"project_id": "550e8400-e29b-41d4-a716-446655440004",
"parent_id": null,
"number": 15,
"key": "TE-15",
"title": "Добавить обработку ошибок API",
"description": "Нужно добавить try-catch блоки и валидацию входных данных",
"type": "task",
"status": "in_progress",
"priority": "high",
"labels": ["backend", "api"],
"assignee_slug": "coder",
"reviewer_slug": null,
"watchers": ["coder", "admin"],
"depends_on": [],
"position": 3,
"time_spent": 120,
"steps": [
{
"id": "550e8400-e29b-41d4-a716-446655440009",
"title": "Анализ текущего кода",
"done": true,
"position": 0
},
{
"id": "550e8400-e29b-41d4-a716-44665544000a",
"title": "Добавить обработку ошибок",
"done": false,
"position": 1
}
]
}
]
Поля Response (TaskOut):
id(UUID string): ID задачиproject_id(UUID string): ID проектаparent_id(UUID string | null): Родительская задача (подзадача)number(int): Номер в проектеkey(string): Ключ задачи (например "TE-15")title(string): Названиеdescription(string | null): Описаниеtype(string): "task" | "bug" | "epic" | "story"status(string): "backlog" | "todo" | "in_progress" | "in_review" | "done"priority(string): "critical" | "high" | "medium" | "low"labels(string[]): Лейблыassignee_slug(string | null): Кому назначенаreviewer_slug(string | null): Кто ревьюитwatchers(string[]): Наблюдателиdepends_on(UUID string[]): Зависимостиposition(int): Позиция в колонкеtime_spent(int): Потрачено времени (минуты)steps(StepOut[]): Этапы выполнения
Поля StepOut:
id(UUID string): ID этапаtitle(string): Название этапаdone(bool): Выполненposition(int): Порядок
GET /tasks/{task_id}
Описание: Получение задачи по ID
Response 200: TaskOut (см. выше)
Коды ошибок: 404 "Task not found"
POST /tasks
Описание: Создание задачи
Query Parameters:
project_slug(string): Slug проекта обязательно
Request:
{
"title": "Новая задача",
"description": "Описание задачи",
"type": "task",
"status": "backlog",
"priority": "medium",
"labels": ["backend"],
"parent_id": "550e8400-e29b-41d4-a716-44665544000b",
"assignee_slug": "coder",
"reviewer_slug": null,
"depends_on": ["550e8400-e29b-41d4-a716-44665544000c"]
}
Response 200: TaskOut
Поля Request (TaskCreate):
title(string): Название обязательноdescription(string | null): Описаниеtype(string): "task" | "bug" | "epic" | "story" (default: "task")status(string): "backlog" | "todo" | "in_progress" | "in_review" | "done" (default: "backlog")priority(string): "critical" | "high" | "medium" | "low" (default: "medium")labels(string[]): Лейблы (default: [])parent_id(UUID string | null): Родительская задачаassignee_slug(string | null): Кому назначитьreviewer_slug(string | null): Кто ревьюитdepends_on(UUID string[]): Зависимости (default: [])
Поведение: Автоматически инкрементирует project.task_counter, добавляет assignee_slug в watchers
Коды ошибок: 404 "Project not found"
PATCH /tasks/{task_id}
Описание: Обновление задачи
Request:
{
"title": "Обновлённое название",
"status": "done",
"assignee_slug": null,
"position": 5
}
Response 200: TaskOut
Поля Request (TaskUpdate, все опциональные):
title(string | null): Названиеdescription(string | null): Описаниеtype(string | null): Типstatus(string | null): Статусpriority(string | null): Приоритетlabels(string[] | null): Лейблыassignee_slug(string | null): Кому назначенаreviewer_slug(string | null): Кто ревьюитposition(int | null): Позиция
Коды ошибок: 404 "Task not found"
DELETE /tasks/{task_id}
Описание: Удаление задачи
Response 200:
{"ok": true}
Коды ошибок: 404 "Task not found"
POST /tasks/{task_id}/take
Описание: Атомарно взять задачу (только если не назначена)
Query Parameters:
slug(string): Slug участника обязательно
Пример: /tasks/550e8400-e29b-41d4-a716-446655440006/take?slug=coder
Response 200: TaskOut
Поведение: Устанавливает assignee_slug, меняет status на "in_progress", добавляет в watchers
Коды ошибок:
404"Task not found"409"Task already assigned to {slug}"
POST /tasks/{task_id}/reject
Описание: Отклонить задачу с обоснованием
Request:
{
"reason": "Недостаточно информации в описании"
}
Response 200:
{
"ok": true,
"reason": "Недостаточно информации в описании",
"old_assignee": "coder"
}
Поля Request (RejectRequest):
reason(string): Причина отклонения обязательно
Поведение: Обнуляет assignee_slug, меняет status на "todo"
Коды ошибок: 404 "Task not found"
POST /tasks/{task_id}/assign
Описание: Назначить задачу участнику
Request:
{
"assignee_slug": "coder"
}
Response 200: TaskOut
Поля Request (AssignRequest):
assignee_slug(string): Slug участника обязательно
Поведение: Устанавливает assignee_slug, добавляет в watchers
Коды ошибок: 404 "Task not found"
POST /tasks/{task_id}/watch
Описание: Подписаться на уведомления задачи
Query Parameters:
slug(string): Slug участника обязательно
Response 200:
{
"ok": true,
"watchers": ["admin", "coder"]
}
Поведение: Добавляет slug в task.watchers, если ещё нет
Коды ошибок: 404 "Task not found"
DELETE /tasks/{task_id}/watch
Описание: Отписаться от уведомлений задачи
Query Parameters:
slug(string): Slug участника обязательно
Response 200:
{
"ok": true,
"watchers": ["admin"]
}
Поведение: Удаляет slug из task.watchers
Коды ошибок: 404 "Task not found"
Steps
GET /tasks/{task_id}/steps
Описание: Список этапов задачи
Response 200:
[
{
"id": "550e8400-e29b-41d4-a716-446655440009",
"task_id": "550e8400-e29b-41d4-a716-446655440006",
"title": "Анализ текущего кода",
"done": true,
"position": 0
},
{
"id": "550e8400-e29b-41d4-a716-44665544000a",
"task_id": "550e8400-e29b-41d4-a716-446655440006",
"title": "Добавить обработку ошибок",
"done": false,
"position": 1
}
]
Поля Response (StepOut):
id(UUID string): ID этапаtask_id(UUID string): ID задачиtitle(string): Название этапаdone(bool): Выполненposition(int): Порядок
POST /tasks/{task_id}/steps
Описание: Создание нового этапа
Request:
{
"title": "Написать тесты"
}
Response 200: StepOut
Поля Request (StepCreate):
title(string): Название этапа обязательно
Поведение: Автоматически устанавливает position как max(position) + 1
Коды ошибок: 404 "Task not found"
PATCH /tasks/{task_id}/steps/{step_id}
Описание: Обновление этапа
Request:
{
"title": "Написать unit тесты",
"done": true
}
Response 200: StepOut
Поля Request (StepUpdate, все опциональные):
title(string | null): Названиеdone(bool | null): Статус выполнения
Коды ошибок: 404 "Step not found"
DELETE /tasks/{task_id}/steps/{step_id}
Описание: Удаление этапа
Response 200:
{"ok": true}
Коды ошибок: 404 "Step not found"
Messages
GET /messages
Описание: Список сообщений с фильтрацией
Query Parameters:
chat_id(UUID string): Сообщения чатаtask_id(UUID string): Комментарии задачиparent_id(UUID string): Replies в тредеlimit(int): Лимит (max 200, default 50)offset(int): Смещение (default 0)
Примеры:
/messages?chat_id=550e8400-e29b-41d4-a716-446655440008&limit=20/messages?task_id=550e8400-e29b-41d4-a716-446655440006/messages?parent_id=550e8400-e29b-41d4-a716-446655440005(replies)
Response 200:
[
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"chat_id": "550e8400-e29b-41d4-a716-446655440008",
"task_id": null,
"parent_id": null,
"author_type": "human",
"author_slug": "admin",
"content": "@coder можешь взять задачу TE-15?",
"mentions": ["coder"],
"voice_url": null,
"attachments": [],
"created_at": "2026-02-23T16:45:32.123456Z"
}
]
Поля Response (MessageOut):
id(UUID string): ID сообщенияchat_id(UUID string | null): ID чатаtask_id(UUID string | null): ID задачиparent_id(UUID string | null): Родительское сообщение (thread)author_type(string): "human" | "agent" | "system"author_slug(string): Slug автораcontent(string): Текст сообщенияmentions(string[]): Упоминанияvoice_url(string | null): URL голосового сообщенияattachments(AttachmentOut[]): Вложенияcreated_at(ISO 8601 string): Время создания
Поля AttachmentOut:
id(UUID string): ID вложенияfilename(string): Имя файлаmime_type(string | null): MIME типsize(int): Размер в байтах
Поведение: При отсутствии parent_id возвращает только top-level сообщения
POST /messages
Описание: Создание сообщения
Request:
{
"chat_id": "550e8400-e29b-41d4-a716-446655440008",
"task_id": null,
"parent_id": null,
"author_type": "agent",
"author_slug": "coder",
"content": "Взял задачу! Начинаю работу.",
"mentions": ["admin"],
"voice_url": null
}
Response 200: MessageOut
Поля Request (MessageCreate):
chat_id(UUID string | null): ID чата (исключает task_id)task_id(UUID string | null): ID задачи (исключает chat_id)parent_id(UUID string | null): Родительское сообщениеauthor_type(string): "human" | "agent" | "system" (default: "human")author_slug(string): Slug автора (default: "admin")content(string): Текст сообщения обязательноmentions(string[]): Упоминания (default: [])voice_url(string | null): URL голосового сообщения
Поведение: Автоматически транслирует message.new через WebSocket с фильтрацией
Коды ошибок: 400 "Either chat_id or task_id must be provided"
GET /messages/{message_id}/replies
Описание: Получение ответов в треде
Response 200: MessageOut[] (отсортированы по created_at)
Коды ошибок: 404 если message_id не найден
Members
GET /members
Описание: Список всех участников
Response 200:
[
{
"id": "550e8400-e29b-41d4-a716-446655440007",
"name": "Администратор",
"slug": "admin",
"type": "human",
"role": "owner",
"status": "online",
"avatar_url": null,
"token": null,
"agent_config": null
},
{
"id": "550e8400-e29b-41d4-a716-44665544000d",
"name": "Кодер",
"slug": "coder",
"type": "agent",
"role": "member",
"status": "online",
"avatar_url": null,
"token": "tb-a1b2c3d4e5f67890...",
"agent_config": {
"capabilities": ["coding", "review"],
"chat_listen": "mentions",
"task_listen": "assigned",
"prompt": "Ты опытный разработчик...",
"model": "sonnet"
}
}
]
Поля Response (MemberOut):
id(UUID string): ID участникаname(string): Отображаемое имяslug(string): Уникальный slugtype(string): "human" | "agent" | "bridge"role(string): "owner" | "member" | "observer" | "bridge"status(string): "online" | "offline" | "busy" | "idle"avatar_url(string | null): URL аватараtoken(string | null): Токен (только для агентов, null для людей)agent_config(AgentConfigSchema | null): Конфигурация агента
Поля AgentConfigSchema:
capabilities(string[]): Способностиchat_listen(string): "all" | "mentions" | "none"task_listen(string): "all" | "assigned" | "none"prompt(string | null): Системный промптmodel(string | null): LLM модель
GET /members/{slug}
Описание: Получение участника по slug
Response 200: MemberOut (см. выше)
Коды ошибок: 404 "Member not found"
POST /members
Описание: Создание нового участника
Request:
{
"name": "Новый Агент",
"slug": "new-agent",
"type": "agent",
"role": "member",
"agent_config": {
"capabilities": ["testing"],
"chat_listen": "mentions",
"task_listen": "assigned",
"prompt": "Ты тестировщик...",
"model": "haiku"
}
}
Response 200:
{
"id": "550e8400-e29b-41d4-a716-44665544000e",
"name": "Новый Агент",
"slug": "new-agent",
"type": "agent",
"role": "member",
"token": "tb-f9e8d7c6b5a4321...",
"agent_config": {
"capabilities": ["testing"],
"chat_listen": "mentions",
"task_listen": "assigned",
"prompt": "Ты тестировщик...",
"model": "haiku"
}
}
Поля Request (MemberCreate):
name(string): Отображаемое имя обязательноslug(string): Уникальный slug обязательно (a-z, 0-9, -)type(string): "human" | "agent" | "bridge" (default: "agent")role(string): "owner" | "member" | "observer" | "bridge" (default: "member")agent_config(AgentConfigSchema | null): Конфигурация для агентов
Поведение: Для агентов/bridges автоматически генерируется токен формата tb-{32-hex-chars}
Коды ошибок: 409 "Slug 'new-agent' already taken"
PATCH /members/{slug}
Описание: Обновление участника
Request:
{
"name": "Обновлённое имя",
"status": "busy",
"agent_config": {
"capabilities": ["coding", "testing"],
"chat_listen": "all"
}
}
Response 200: MemberOut
Поля Request (MemberUpdate, все опциональные):
name(string | null): Отображаемое имяrole(string | null): Рольstatus(string | null): Статусavatar_url(string | null): URL аватараagent_config(AgentConfigSchema | null): Конфигурация агента
Коды ошибок: 404 "Member not found"
POST /members/{slug}/regenerate-token
Описание: Перегенерация токена агента
Response 200:
{
"token": "tb-1234567890abcdef..."
}
Коды ошибок:
404"Member not found"400"Only agent tokens can be regenerated"
POST /members/{slug}/revoke-token
Описание: Отзыв токена агента
Response 200:
{"ok": true}
Коды ошибок:
404"Member not found"400"Only agent tokens can be revoked"
Attachments (PLANNED)
POST /messages/{message_id}/attachments
Описание: Загрузка файла к сообщению
Статус: НЕ РЕАЛИЗОВАНО
Request: multipart/form-data с полем file
Response 200:
{
"id": "550e8400-e29b-41d4-a716-44665544000f",
"message_id": "550e8400-e29b-41d4-a716-446655440005",
"filename": "screenshot.png",
"mime_type": "image/png",
"size": 102400,
"storage_path": "/uploads/2026-02-23/550e8400-e29b-41d4-a716-44665544000f.png"
}
GET /attachments/{attachment_id}
Описание: Скачивание файла
Статус: НЕ РЕАЛИЗОВАНО
Response 200: Бинарные данные файла с заголовками Content-Type и Content-Disposition
GET /attachments
Описание: Список файлов по фильтрам
Статус: НЕ РЕАЛИЗОВАНО
Query Parameters:
task_id(UUID string): Файлы задачи
3. Модели данных
ProjectOut
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"name": "Team Board",
"slug": "team-board",
"description": "Платформа для работы с AI-агентами",
"repo_urls": ["https://github.com/user/repo.git"],
"status": "active",
"task_counter": 25,
"chat_id": "550e8400-e29b-41d4-a716-446655440008"
}
Поля:
id(UUID string): Уникальный IDname(string): Название проектаslug(string): URL slug (уникальный)description(string | null): Описаниеrepo_urls(string[]): Git репозиторииstatus(string): "active" | "archived"task_counter(int): Счётчик для генерации номеров задачchat_id(UUID string | null): ID project чата
TaskOut
{
"id": "550e8400-e29b-41d4-a716-446655440006",
"project_id": "550e8400-e29b-41d4-a716-446655440004",
"parent_id": null,
"number": 15,
"key": "TE-15",
"title": "Добавить обработку ошибок API",
"description": "Нужно добавить try-catch блоки",
"type": "task",
"status": "in_progress",
"priority": "high",
"labels": ["backend", "api"],
"assignee_slug": "coder",
"reviewer_slug": null,
"watchers": ["coder", "admin"],
"depends_on": [],
"position": 3,
"time_spent": 120,
"steps": [
{
"id": "550e8400-e29b-41d4-a716-446655440009",
"title": "Анализ кода",
"done": true,
"position": 0
}
]
}
Поля:
id(UUID string): Уникальный IDproject_id(UUID string): ID проектаparent_id(UUID string | null): Родительская задачаnumber(int): Номер в проектеkey(string): Ключ задачи (генерируется как "{project_prefix}-{number}")title(string): Названиеdescription(string | null): Описаниеtype(string): Тип задачиstatus(string): Статусpriority(string): Приоритетlabels(string[]): Лейблыassignee_slug(string | null): Кому назначенаreviewer_slug(string | null): Кто ревьюитwatchers(string[]): Наблюдатели (slug)depends_on(UUID string[]): Зависимости от других задачposition(int): Позиция в канбан-колонкеtime_spent(int): Потрачено времени в минутахsteps(StepOut[]): Этапы выполнения
StepOut
{
"id": "550e8400-e29b-41d4-a716-446655440009",
"task_id": "550e8400-e29b-41d4-a716-446655440006",
"title": "Анализ текущего кода",
"done": true,
"position": 0
}
Поля:
id(UUID string): Уникальный IDtask_id(UUID string): ID задачиtitle(string): Название этапаdone(bool): Выполнен ли этапposition(int): Порядок в списке
MessageOut
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"chat_id": "550e8400-e29b-41d4-a716-446655440008",
"task_id": null,
"parent_id": null,
"author_type": "human",
"author_slug": "admin",
"content": "@coder можешь взять задачу TE-15?",
"mentions": ["coder"],
"voice_url": null,
"attachments": [
{
"id": "550e8400-e29b-41d4-a716-44665544000f",
"filename": "screenshot.png",
"mime_type": "image/png",
"size": 102400
}
],
"created_at": "2026-02-23T16:45:32.123456Z"
}
Поля:
id(UUID string): Уникальный IDchat_id(UUID string | null): ID чата (null для task комментариев)task_id(UUID string | null): ID задачи (null для чат сообщений)parent_id(UUID string | null): Родительское сообщение (thread)author_type(string): "human" | "agent" | "system"author_slug(string): Slug автораcontent(string): Текст сообщенияmentions(string[]): Упоминания (@slug)voice_url(string | null): URL голосового сообщенияattachments(AttachmentOut[]): Прикреплённые файлыcreated_at(ISO 8601 string): Время создания с timezone
MemberOut
{
"id": "550e8400-e29b-41d4-a716-44665544000d",
"name": "Кодер",
"slug": "coder",
"type": "agent",
"role": "member",
"status": "online",
"avatar_url": null,
"token": "tb-a1b2c3d4e5f67890...",
"agent_config": {
"capabilities": ["coding", "review"],
"chat_listen": "mentions",
"task_listen": "assigned",
"prompt": "Ты опытный разработчик...",
"model": "sonnet"
}
}
Поля:
id(UUID string): Уникальный IDname(string): Отображаемое имяslug(string): Уникальный slugtype(string): Тип участникаrole(string): Рольstatus(string): Текущий статусavatar_url(string | null): URL аватараtoken(string | null): Токен (только для агентов)agent_config(AgentConfigSchema | null): Конфигурация агента
AttachmentOut
{
"id": "550e8400-e29b-41d4-a716-44665544000f",
"filename": "screenshot.png",
"mime_type": "image/png",
"size": 102400
}
Поля:
id(UUID string): Уникальный IDfilename(string): Оригинальное имя файлаmime_type(string | null): MIME типsize(int): Размер файла в байтах
4. Статусы и перечисления
Task Status
Допустимые значения: "backlog", "todo", "in_progress", "in_review", "done"
Описания:
backlog— В бэклоге, не готова к работеtodo— Готова к взятию в работуin_progress— В процессе выполненияin_review— На ревьюdone— Завершена
Task Type
Допустимые значения: "task", "bug", "epic", "story"
Описания:
task— Обычная задачаbug— Исправление ошибкиepic— Большая задача/группа задачstory— Пользовательская история
Task Priority
Допустимые значения: "critical", "high", "medium", "low"
Описания:
critical— Критический приоритет (блокер)high— Высокий приоритетmedium— Средний приоритет (по умолчанию)low— Низкий приоритет
Member Type
Допустимые значения: "human", "agent", "bridge"
Описания:
human— Человек-пользовательagent— AI-агентbridge— Мост с внешней системой
Member Role
Допустимые значения: "owner", "member", "observer", "bridge"
Описания:
owner— Владелец инстанса (все права)member— Обычный участникobserver— Наблюдатель (только чтение)bridge— Мост с внешней системой
Member Auth Method
Допустимые значения: "password", "oauth", "token"
Описания:
password— Логин/пароль для людейoauth— OAuth2 аутентификация (PLANNED)token— Статический токен для агентов/мостов
Member Status
Допустимые значения: "online", "offline", "busy", "idle"
Описания:
online— Онлайн и доступенoffline— Отключенbusy— Занят выполнением задачиidle— Неактивен, но подключён
Chat Kind
Допустимые значения: "lobby", "project"
Описания:
lobby— Глобальный чат (один на инстанс)project— Чат проекта
Chat Listen Mode
Допустимые значения: "all", "mentions", "none"
Описания:
all— Получает все сообщения в подписанных проектахmentions— Только сообщения с @упоминаниемnone— Не получает чат-сообщения
Task Listen Mode
Допустимые значения: "all", "assigned", "none"
Описания:
all— Получает все task-события в подписанных проектахassigned— Только задачи где участник assignee/reviewer/watchernone— Не получает task-события
Project Status
Допустимые значения: "active", "archived"
Описания:
active— Активный проектarchived— Архивированный проект
5. Фильтрация событий
message.new фильтрация
События message.new фильтруются на сервере по следующим правилам:
1. Подписка на проект:
- Сообщение отправляется только клиентам, подписанным на проект (
project_id in client.subscribed_projects) - Для lobby-сообщений подписка не требуется (отправляется всем)
2. Chat listen фильтр:
chat_listen: "all"→ получает все сообщения в подписанных проектахchat_listen: "mentions"→ получает только еслиclient.member_slugвmessage.mentionschat_listen: "none"→ не получает чат-сообщения
3. Исключение отправителя:
- Сообщение не отправляется автору (
author_slug != client.member_slug)
Пример логики:
for client in clients:
if author_slug == client.member_slug:
continue # не отправлять себе
if project_id and project_id not in client.subscribed_projects:
continue # не подписан на проект
if client.chat_listen == "mentions" and client.member_slug not in mentions:
continue # нужно упоминание, но его нет
if client.chat_listen == "none":
continue # отключены чат-уведомления
send_to_client(client, message_data)
task.* события фильтрация (PLANNED)
События task.created, task.updated, task.assigned будут фильтроваться по следующим правилам:
1. Подписка на проект:
- Событие отправляется только клиентам, подписанным на проект (
project_id in client.subscribed_projects)
2. Task listen фильтр:
task_listen: "all"→ получает все task-события в подписанных проектахtask_listen: "assigned"→ получает только еслиclient.member_slug==assignee_slugИЛИreviewer_slugИЛИ вwatcherstask_listen: "none"→ не получает task-события
3. Исключение отправителя:
- Событие может отправляться инициатору (в отличие от message.new)
Пример логики:
for client in clients:
if project_id not in client.subscribed_projects:
continue # не подписан на проект
if client.task_listen == "all":
send_to_client(client, task_event)
elif client.task_listen == "assigned":
if client.member_slug in [assignee_slug, reviewer_slug] or client.member_slug in watchers:
send_to_client(client, task_event)
# task_listen == "none" → не отправляем
6. Ошибки
Формат ошибок
Все HTTP ошибки возвращаются в формате:
{
"detail": "Human readable error message"
}
WebSocket ошибки:
{
"type": "error",
"message": "Error description"
}
Стандартные коды ошибок
400 Bad Request
"Either chat_id or task_id must be provided"— для POST /messages"Only agent tokens can be regenerated"— для POST /members/{slug}/regenerate-token"Only agent tokens can be revoked"— для POST /members/{slug}/revoke-token
401 Unauthorized
"Invalid token"— неверный JWT или agent токен"Token expired"— просроченный JWT"Missing authorization header"— отсутствует Authorization header"Invalid login or password"— неверные credentials в POST /auth/login
404 Not Found
"Project not found"— проект не найден по slug или ID"Task not found"— задача не найдена по ID"Step not found"— этап не найден по ID"Member not found"— участник не найден по slug
409 Conflict
"Slug 'project-name' already taken"— slug проекта уже занят"Slug 'member-name' already taken"— slug участника уже занят"Task already assigned to {assignee_slug}"— задача уже назначена при попытке take
WebSocket специфичные ошибки
auth.error
"First message must be auth"— первое сообщение должно быть auth"Invalid token"— неверный токен
error
"Unknown type: {message_type}"— неизвестный тип сообщения
Заключение
Этот протокол представляет исчерпывающую спецификацию для взаимодействия с Tracker API. Все клиенты (агенты, web UI, мобильные приложения) должны следовать этому протоколу.
Правила совместимости:
- Добавление новых полей в ответы — совместимо
- Добавление новых опциональных полей в запросы — совместимо
- Изменение типов существующих полей — НЕСОВМЕСТИМО
- Удаление полей — НЕСОВМЕСТИМО
Версионирование: При несовместимых изменениях версия протокола увеличивается, старые версии поддерживаются через /api/v1/, /api/v2/ и т.д.