docs/specs/API.md
2026-03-13 22:56:04 +01:00

23 KiB
Raw Permalink Blame History

Team Board — REST API спецификация

Общие сведения

Базовый URL: /api/v1/
Аутентификация: Bearer токен в заголовке Authorization или query parameter ?token=
Формат данных: JSON
Middleware: все API endpoints требуют авторизации (кроме /api/v1/auth/login)

Типы токенов:

  • JWT — для web пользователей (срок жизни 72 часа)
  • Bearer токены — для агентов (формат tb-xxxxx, постоянные)

CORS:

  • https://team.uix.su
  • https://dev.team.uix.su
  • http://localhost:3100

Auth — Аутентификация

POST /api/v1/auth/login

Вход в систему для пользователей

Тело запроса:

{
  "login": "string",     // slug или name пользователя
  "password": "string"
}

Ответ (200):

{
  "token": "jwt_token_here",
  "member_id": "uuid",
  "slug": "string", 
  "role": "owner|admin|member"
}

Ошибки: 401 - неверный логин/пароль


Members — Участники (люди + агенты)

GET /api/v1/members

Список всех участников

Query параметры:

  • include_inactive=boolean — включить неактивных (по умолчанию false)

Ответ (200):

[
  {
    "id": "uuid",
    "name": "string",
    "slug": "string",
    "type": "human|agent|bridge",
    "role": "owner|admin|member",
    "status": "online|offline|busy",
    "is_active": true,
    "avatar_url": "string|null",
    "last_seen_at": "iso_timestamp",
    "agent_config": {
      "capabilities": ["string"],
      "labels": ["string"],
      "chat_listen": "all|mentions",
      "task_listen": "all|mentions", 
      "prompt": "string|null",
      "model": "string|null"
    }
  }
]

GET /api/v1/members/{member_id}

Получить участника по ID

Ответ (200): объект Member (как в списке)
Ошибки: 404 - участник не найден

POST /api/v1/members

Создать нового участника (человека или агента)

Тело запроса:

{
  "name": "string",
  "slug": "string",
  "type": "human|agent",    // по умолчанию "agent"
  "role": "owner|admin|member",  // по умолчанию "member"  
  "agent_config": {         // только для type="agent"
    "capabilities": ["string"],
    "labels": ["string"],
    "chat_listen": "all|mentions",
    "task_listen": "all|mentions",
    "prompt": "string|null",
    "model": "string|null"
  }
}

Ответ (201):

{
  "id": "uuid",
  "name": "string",
  "slug": "string", 
  "type": "human|agent",
  "role": "owner|admin|member",
  "token": "tb-xxxxx",    // только при создании агента, показывается один раз
  "agent_config": {...}   // если был передан
}

Ошибки: 409 - slug уже занят

PATCH /api/v1/members/{member_id}

Обновить участника

Тело запроса:

{
  "name": "string|null",
  "role": "string|null",
  "status": "string|null",
  "avatar_url": "string|null",
  "agent_config": {         // только для агентов
    "capabilities": ["string"],
    "labels": ["string"],
    "chat_listen": "all|mentions",
    "task_listen": "all|mentions",
    "prompt": "string|null",
    "model": "string|null"
  }
}

Ответ (200): обновлённый объект Member

Примечания:

  • Изменение agent_config отправляет WebSocket событие config.updated агенту
  • При изменении конфигурации агент получает новые настройки в реальном времени

POST /api/v1/members/{member_id}/regenerate-token

Сгенерировать новый токен для агента

Ответ (200):

{
  "token": "tb-xxxxx"
}

Ошибки: 400 - только для агентов

POST /api/v1/members/{member_id}/revoke-token

Отозвать токен агента

Ответ (200):

{
  "ok": true
}

Ошибки: 400 - только для агентов

PATCH /api/v1/members/me/status

Обновить свой статус (используется агентами)

Query параметры:

  • status=string — новый статус

Ответ (200):

{
  "status": "online|offline|busy"
}

Примечания: отправляет WebSocket событие agent.status всем подключённым клиентам

DELETE /api/v1/members/{member_id}

Удалить участника (soft delete — is_active=false)

Авторизация: только владельцы
Ответ (200): {"ok": true}
Ошибки: 403 - недостаточно прав


Projects — Проекты

GET /api/v1/projects

Список активных проектов

Ответ (200):

[
  {
    "id": "uuid",
    "name": "string",
    "slug": "string",
    "description": "string|null",
    "repo_urls": ["string"],
    "status": "active|archived",
    "task_counter": 42,
    "chat_id": "uuid|null",
    "auto_assign": true
  }
]

GET /api/v1/projects/{project_id}

Получить проект по ID

Ответ (200): объект Project (как в списке)
Ошибки: 404 - проект не найден

POST /api/v1/projects

Создать новый проект

Тело запроса:

{
  "name": "string",
  "slug": "string",
  "description": "string|null",
  "repo_urls": ["string"]
}

Ответ (201): объект Project
Ошибки: 409 - slug уже занят

Примечания:

  • Автоматически создаётся основной чат проекта
  • Отправляет WebSocket событие project.created

PATCH /api/v1/projects/{project_id}

Обновить проект

Тело запроса:

{
  "name": "string|null",
  "slug": "string|null",
  "description": "string|null", 
  "repo_urls": ["string"]|null,
  "status": "active|archived|null",
  "auto_assign": "boolean|null"
}

Ответ (200): обновлённый объект Project
Ошибки: 409 - slug уже занят

DELETE /api/v1/projects/{project_id}

Удалить проект

Авторизация: только владельцы
Ответ (200): {"ok": true}
Ошибки: 403 - недостаточно прав

GET /api/v1/projects/{project_id}/members

Участники проекта

Ответ (200):

[
  {
    "id": "uuid",
    "name": "string", 
    "slug": "string",
    "type": "human|agent|bridge",
    "role": "owner|admin|member"
  }
]

POST /api/v1/projects/{project_id}/members

Добавить участника в проект

Тело запроса:

{
  "member_id": "uuid"
}

Ответ (200): {"ok": true}
Ошибки: 404 - участник не найден, 409 - участник уже в проекте

DELETE /api/v1/projects/{project_id}/members/{member_id}

Исключить участника из проекта

Ответ (200): {"ok": true}
Ошибки: 400 - нельзя исключить владельца, 404 - участник не в проекте


Tasks — Задачи

GET /api/v1/tasks

Список задач с фильтрацией

Query параметры:

  • project_id=uuid — фильтр по проекту
  • status=string — фильтр по статусу (backlog|todo|in_progress|review|done)
  • assignee_id=uuid — фильтр по исполнителю
  • label=string — фильтр по лейблу
  • q=string — поиск по номеру (XX-123) или заголовку

Ответ (200):

[
  {
    "id": "uuid",
    "project": {
      "id": "uuid",
      "slug": "string",
      "name": "string"
    },
    "number": 123,
    "key": "XX-123",
    "title": "string",
    "description": "string|null",
    "type": "task|bug|epic",
    "status": "backlog|todo|in_progress|review|done",
    "priority": "low|medium|high|urgent",
    "labels": ["string"],
    "assignee": {
      "id": "uuid",
      "slug": "string", 
      "name": "string"
    } | null,
    "reviewer": {...} | null,
    "parent": {...} | null,
    "subtasks": [...],
    "steps": [
      {
        "id": "uuid",
        "title": "string",
        "done": true,
        "position": 0
      }
    ],
    "watcher_ids": ["uuid"],
    "depends_on": ["uuid"],
    "position": 0,
    "created_at": "iso_timestamp",
    "updated_at": "iso_timestamp"
  }
]

GET /api/v1/tasks/{task_id}

Получить задачу по ID

Ответ (200): объект Task (как в списке)
Ошибки: 404 - задача не найдена

POST /api/v1/tasks

Создать новую задачу

Query параметры:

  • project_id=uuid — ID проекта (обязательно)

Тело запроса:

{
  "title": "string",
  "description": "string|null",
  "type": "task|bug|epic",         // по умолчанию "task"
  "status": "backlog|todo|in_progress|review|done",  // по умолчанию "backlog"
  "priority": "low|medium|high|urgent",  // по умолчанию "medium"
  "labels": ["string"],
  "parent_id": "uuid|null",
  "assignee_id": "uuid|null",
  "reviewer_id": "uuid|null",
  "depends_on": ["uuid"]
}

Ответ (201): объект Task

Примечания:

  • Автоматически назначается номер задачи (инкрементальный счётчик проекта)
  • При auto_assign=true проекта может автоматически назначаться на агента по лейблам
  • Отправляет WebSocket событие task.created
  • Создаёт системное сообщение в чате проекта и комментариях задачи

PATCH /api/v1/tasks/{task_id}

Обновить задачу

Тело запроса:

{
  "title": "string|null",
  "description": "string|null",
  "type": "string|null",
  "status": "string|null",
  "priority": "string|null", 
  "labels": ["string"]|null,
  "assignee_id": "string|null",
  "reviewer_id": "string|null",
  "position": "number|null"
}

Ответ (200): обновлённый объект Task

Примечания:

  • Каждое изменение записывается в audit log (TaskAction)
  • Отправляет WebSocket событие task.updated
  • Создаёт системные сообщения для значимых изменений (статус, назначение)

DELETE /api/v1/tasks/{task_id}

Удалить задачу

Ответ (200): {"ok": true}
Примечания: отправляет WebSocket событие task.deleted

POST /api/v1/tasks/{task_id}/take

Взять задачу в работу (для агентов)

Условия:

  • Задача не назначена (assignee_id=null)
  • Статус backlog или todo

Действие:

  • assignee_id = текущий пользователь
  • status = in_progress
  • Добавление в watcher_ids

Ответ (200): обновлённый объект Task
Ошибки: 409 - задача уже назначена или неподходящий статус

POST /api/v1/tasks/{task_id}/reject

Отклонить назначенную задачу

Тело запроса:

{
  "reason": "string"
}

Действие:

  • assignee_id = null
  • status = backlog
  • Создание системного сообщения с причиной

Ответ (200): {"ok": true, "reason": "string"}

POST /api/v1/tasks/{task_id}/assign

Назначить задачу на участника

Тело запроса:

{
  "assignee_id": "uuid"
}

Ответ (200): обновлённый объект Task
Ошибки: 404 - участник не найден

POST /api/v1/tasks/{task_id}/watch

Подписаться на уведомления по задаче

Ответ (200):

{
  "ok": true,
  "watcher_ids": ["uuid"]
}

DELETE /api/v1/tasks/{task_id}/watch

Отписаться от уведомлений по задаче

Ответ (200):

{
  "ok": true,
  "watcher_ids": ["uuid"] 
}

POST /api/v1/tasks/{task_id}/links

Создать связь между задачами (зависимость)

Тело запроса:

{
  "target_id": "uuid",
  "link_type": "blocks|depends_on|relates_to"
}

Ответ (201):

{
  "id": "uuid",
  "source_id": "uuid",
  "target_id": "uuid", 
  "link_type": "string",
  "target_key": "XX-123",
  "target_title": "string",
  "source_key": "XX-124",
  "source_title": "string"
}

Ошибки: 400 - нельзя связать задачу с самой собой, 409 - связь уже существует


Steps — Этапы задач (чеклист)

GET /api/v1/tasks/{task_id}/steps

Список этапов задачи

Ответ (200):

[
  {
    "id": "uuid",
    "title": "string",
    "done": true,
    "position": 0
  }
]

POST /api/v1/tasks/{task_id}/steps

Создать новый этап

Тело запроса:

{
  "title": "string"
}

Ответ (201): объект Step
Ошибки: 404 - задача не найдена

PATCH /api/v1/tasks/{task_id}/steps/{step_id}

Обновить этап

Тело запроса:

{
  "title": "string|null",
  "done": "boolean|null"
}

Ответ (200): обновлённый объект Step

DELETE /api/v1/tasks/{task_id}/steps/{step_id}

Удалить этап

Ответ (200): {"ok": true}


Messages — Сообщения (чаты + комментарии к задачам)

GET /api/v1/messages

Список сообщений

Query параметры:

  • chat_id=uuid — сообщения из чата
  • task_id=uuid — комментарии к задаче
  • parent_id=uuid — ответы в треде
  • limit=number — лимит (по умолчанию 50, макс 200)
  • offset=number — смещение (по умолчанию 0)

Ответ (200):

[
  {
    "id": "uuid",
    "chat_id": "uuid|null",
    "task_id": "uuid|null",
    "parent_id": "uuid|null",
    "author_type": "human|agent|system",
    "author_id": "uuid|null",
    "author": {
      "id": "uuid",
      "slug": "string",
      "name": "string"
    } | null,
    "actor": {...} | null,    // для системных сообщений
    "content": "string",
    "thinking": "string|null",
    "mentions": [
      {
        "id": "uuid",
        "slug": "string", 
        "name": "string"
      }
    ],
    "voice_url": "string|null",
    "attachments": [
      {
        "id": "uuid",
        "filename": "string",
        "mime_type": "string",
        "size": 1024
      }
    ],
    "created_at": "iso_timestamp"
  }
]

Примечания: возвращает последние N сообщений в хронологическом порядке

POST /api/v1/messages

Отправить сообщение

Тело запроса:

{
  "chat_id": "uuid|null",       // либо chat_id, либо task_id обязательно
  "task_id": "uuid|null", 
  "parent_id": "uuid|null",     // для ответов в треде
  "content": "string",
  "thinking": "string|null",    // внутренние размышления (для агентов)
  "mentions": ["member_id"],    // упоминания по ID
  "voice_url": "string|null",   // ссылка на голосовое сообщение
  "attachments": [              // прикреплённые файлы из /upload
    {
      "file_id": "uuid",
      "filename": "string", 
      "mime_type": "string",
      "size": 1024,
      "storage_name": "string"
    }
  ]
}

Ответ (201): объект Message
Ошибки: 400 - не указан chat_id или task_id, 401 - не авторизован

Примечания:

  • author_id берётся из токена авторизации
  • Отправляет WebSocket событие message.new
  • Для project чатов событие фильтруется по подпискам участников

GET /api/v1/messages/{message_id}/replies

Ответы в треде

Ответ (200): массив объектов Message (отсортированы по created_at)


Attachments — Файловые вложения

POST /api/v1/upload

Загрузить файл

Content-Type: multipart/form-data
Поля:

  • file — файловое поле

Ответ (200):

{
  "file_id": "uuid",
  "filename": "string",
  "mime_type": "string", 
  "size": 1024,
  "storage_name": "string"
}

Ошибки: 413 - файл слишком большой (лимит 50MB)

Примечания:

  • Файл сохраняется в /data/uploads/
  • Возвращаемые данные используются в attachments при создании сообщений
  • Файл пока не привязан к сообщению

GET /api/v1/attachments/{attachment_id}/download

Скачать файл

Query параметры:

  • token=string — токен авторизации (опционально, для img src)

Ответ (200): файл с правильными заголовками
Ошибки: 404 - файл не найден


Project Files — Файлы проектов

GET /api/v1/projects/{project_id}/files

Список файлов проекта

Query параметры:

  • search=string — поиск по имени файла

Ответ (200):

[
  {
    "id": "uuid",
    "filename": "string",
    "description": "string|null", 
    "mime_type": "string",
    "size": 1024,
    "uploaded_by": {
      "id": "uuid",
      "slug": "string",
      "name": "string"
    },
    "created_at": "iso_timestamp",
    "updated_at": "iso_timestamp"
  }
]

POST /api/v1/projects/{project_id}/files

Загрузить файл в проект

Content-Type: multipart/form-data
Поля:

  • file — файловое поле
  • description — описание файла (опционально)

Ответ (201): объект ProjectFile
Ошибки: 413 - файл слишком большой, 400 - некорректное имя файла

Примечания:

  • Файлы сохраняются в /data/projects/{project_slug}/
  • При загрузке файла с существующим именем — перезаписывается

GET /api/v1/projects/{project_id}/files/{file_id}

Информация о файле

Ответ (200): объект ProjectFile

GET /api/v1/projects/{project_id}/files/{file_id}/download

Скачать файл проекта

Ответ (200): файл с правильными заголовками
Ошибки: 404 - файл не найден

PATCH /api/v1/projects/{project_id}/files/{file_id}

Обновить описание файла

Тело запроса:

{
  "description": "string|null"
}

Ответ (200): обновлённый объект ProjectFile

DELETE /api/v1/projects/{project_id}/files/{file_id}

Удалить файл проекта

Ответ (200): {"ok": true}


Labels — Глобальные лейблы

GET /api/v1/labels

Список всех лейблов

Ответ (200):

[
  {
    "id": "uuid", 
    "name": "string",
    "color": "#6366f1"
  }
]

POST /api/v1/labels

Создать новый лейбл

Тело запроса:

{
  "name": "string",
  "color": "#6366f1"    // по умолчанию
}

Ответ (201): объект Label

PATCH /api/v1/labels/{label_id}

Обновить лейбл

Тело запроса:

{
  "name": "string|null",
  "color": "string|null"
}

Ответ (200): обновлённый объект Label

DELETE /api/v1/labels/{label_id}

Удалить лейбл

Ответ (200): {"ok": true}

POST /api/v1/tasks/{task_id}/labels/{label_id}

Добавить лейбл к задаче

Ответ (200): {"ok": true}

DELETE /api/v1/tasks/{task_id}/labels/{label_id}

Убрать лейбл с задачи

Ответ (200): {"ok": true}


Utility Endpoints

GET /health

Health check

Авторизация: не требуется
Ответ (200):

{
  "status": "ok",
  "version": "0.2.0"
}

GET /docs

Swagger UI документация

Авторизация: не требуется
Доступность: только в dev режиме


Коды ошибок

  • 400 — Bad Request (некорректные данные)
  • 401 — Unauthorized (не авторизован или неверный токен)
  • 403 — Forbidden (недостаточно прав)
  • 404 — Not Found (ресурс не найден)
  • 409 — Conflict (конфликт данных, например дублирование slug)
  • 413 — Payload Too Large (файл слишком большой)
  • 500 — Internal Server Error (внутренняя ошибка сервера)

Middleware и логирование

Логирование запросов: все HTTP запросы логируются с телом (для POST/PUT/PATCH), временем выполнения и кодом ответа

Извлечение участника: middleware автоматически извлекает Member из JWT/agent token и сохраняет в request.state.member

CORS: автоматически обрабатывается для разрешённых origins

Этот документ описывает API на основе исходного кода в /root/projects/team-board/tracker/src/tracker/api/ на дату создания спецификации.

Обновление slug (добавлено 2026-03-13)

PATCH /api/v1/members/{member_id} теперь принимает поле slug.

  • Валидация: slug уникален, при конфликте → 409
  • Формат: lowercase, только [a-z0-9_-]