Документация AlgoBoard

Параметр Значение
Контракт algoboard.v1
Обновлено 9 августа 2026 года
Транспорт ConnectRPC unary, protobuf JSON
Локальный Base URL http://localhost:8080
Источник backend/proto/algoboard/v1/*.proto

Руководства по продукту

Практические руководства по объектам, workflow, полям, расчётам, доступу, автоматизациям и остальным разделам настроек опубликованы отдельными страницами в документации AlgoBoard. Этот файл содержит только API и материалы для программного подключения.

Быстрый запрос

  1. Откройте «Настройки → API и агенты».
  2. Создайте Agent, назначьте роли и capability policy.
  3. Скопируйте секрет первого токена: повторно получить его нельзя.
  4. Передавайте токен в заголовке Authorization.
export ALGOBOARD_TOKEN='ab_agent_...'

curl --fail-with-body \
  -X POST http://localhost:8080/algoboard.v1.BoardService/GetAppState \
  -H "Authorization: Bearer ${ALGOBOARD_TOKEN}" \
  -H "Content-Type: application/json" \
  -H "Connect-Protocol-Version: 1" \
  -d '{}'

Поля JSON записываются в lowerCamelCase: projectId, storyPoints, customFieldValues. Тело пустого unary-запроса — {}.

Аутентификация и права

Браузерная сессия использует HttpOnly cookie algoboard_session. CLI и агенты передают Authorization: Bearer <token>.

Суперадминистратор — отдельная учётная запись платформы: у неё AuthSession.isSuperAdmin = true, заполнены accountEmail и accountLogin, но отсутствуют member и workspace. Она может вызывать только GetSession, Logout, ChangePassword и методы с префиксом Admin. Обычные участники и agent tokens не имеют доступа к superadmin RPC, а суперадминистратор не может вызывать tenant RPC.

Scopes агентского токена:

Scope Что разрешает
read Состояние проекта, карточки, отчёты и другие read-only вызовы
write Создание и изменение карточек, комментарии, спринты и доски
admin Схема процесса, автоматизации, интеграции, импорт и токены; автоматически включает read и write

Права агента проверяются в пять слоёв:

  1. scope агентского токена ограничивает технический класс вызовов;
  2. capability policy agent principal задаёт для read, write, admin решение allow, ask или deny;
  3. настраиваемые роли проекта дают атомарные разрешения вроде issue.view, issue.move, templates.manage или integrations.manage;
  4. разрешения полей ограничивают просмотр и изменение системных и созданных данных карточки;
  5. ACL конкретного представления дополнительно ограничивает операции с этой доской.

Роли назначаются группам, участник получает объединение разрешений всех своих групп. Каталог доступных разрешений приходит в projectState.permissionCatalog, итог текущего участника — в projectState.currentPermissions. Пока в проекте нет ни одной настраиваемой роли, действует совместимый режим Owner/Editor/Viewer. После создания первой роли права не подставляются автоматически: их нужно явно назначить группам. Владелец рабочего пространства получает полный стартовый доступ, только пока ему не назначена ни одна настраиваемая роль; после первого назначения действует обычная редактируемая сумма ролей. Право назначать и изменять владельцев вынесено отдельно в workspace.owners.manage.

Агент использует отдельного служебного участника с principalType=agent; его действия не приписываются владельцу. Решение ask возвращает failed_precondition с одноразовым approval_id до вызова доменного handler, а denypermission_denied. После человеческого approve агент обязан повторить точный запрос с X-Algoboard-Approval; сервер заново проверит RBAC и текущее состояние перед исполнением. admin-токен сам по себе не расширяет agent policy или роль проекта. И наоборот, проектное разрешение не обходит недостающий scope токена.

Исключение — board-вызовы внутри управляемого AgentSkillInvocation. После ClaimAgentSkillInvocation runner получает delegationToken, привязанный к invocation, runner и номеру попытки. Сервер сохраняет Agent principal для авторства и аудита, но вычисляет разрешения по активному requestedByMemberId: используются его текущие проектные роли, field permissions, view ACL и workspace-роль. Это не объединение прав Agent и человека и не snapshot: отзыв роли действует на следующий RPC. Токен перестаёт проходить аутентификацию после FinishAgentSkillInvocation, истечения lease, смены попытки, отзыва постоянного Agent token или деактивации участника. Account/security, superadmin и control-plane управления агентами через эту делегацию недоступны.

Формат ответа и ошибок

Успешный вызов возвращает protobuf JSON без дополнительной оболочки.

{
  "issue": {
    "id": "6e1d...",
    "key": "TEAM-42",
    "title": "Проверить импорт"
  }
}

Ошибка Connect содержит code и message:

{
  "code": "failed_precondition",
  "message": "Перед переходом назначьте исполнителя"
}

Основные коды:

Код Причина Что делать клиенту
unauthenticated Нет токена, токен истёк или отозван Получить новый токен
permission_denied Не хватает scope или роли Не повторять запрос без изменения прав
invalid_argument Некорректные поля или ID Исправить тело запроса
not_found Объект удалён или недоступен Обновить состояние проекта
failed_precondition Workflow запрещает действие либо Agent ждёт approval_id Устранить условие или подтвердить receipt и повторить точный запрос
internal Непредвиденная ошибка сервера Повторить безопасный read; write сначала сверить

Методы Create* не принимают idempotency key. После сетевой ошибки сначала прочитайте состояние и только затем решайте, повторять ли запрос.

Основные вызовы

Все пути начинаются с:

POST /algoboard.v1.BoardService/{Method}

Сессия и состояние

Метод Scope Результат
Register публичный Создаёт workspace, владельца и cookie-сессию
Login публичный Вход по email или уникальному логину, создаёт cookie-сессию
GetSession read Текущий участник и workspace
Logout read Удаляет браузерную сессию
ChangePassword self Проверяет текущий пароль и устанавливает новый
GetAppState read Снимок выбранного проекта, отфильтрованный по currentPermissions
RunQuery read Безопасный серверный select из карточек или сотрудников
GetReports read Каталог встроенных и пользовательских DSL-отчётов
CreateReport reports.manage Создаёт пользовательский DSL-отчёт
UpdateReport reports.manage Редактирует пользовательский или встроенный отчёт
DeleteReport reports.manage Удаляет пользовательский отчёт
PreviewReport reports.view Выполняет сохранённый DSL и возвращает предпросмотр
GetAgentManifest публичный Машиночитаемый список agent tools и scopes
AdminListTenants superadmin Тенанты и агрегаты людей, аккаунтов и проектов
AdminListTenantPeople superadmin Люди и состояние их учётных записей
AdminResetTenantMemberPassword superadmin Создаёт доступ или принудительно меняет пароль

Перед изменяющими запросами вызовите GetAppState. Ответ содержит актуальные ID проектов, цветов, колонок, досок, участников, полей, Wiki-статей, правил и интеграций.

curl --fail-with-body \
  -X POST http://localhost:8080/algoboard.v1.BoardService/GetAppState \
  -H "Authorization: Bearer ${ALGOBOARD_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"PROJECT_UUID"}'

Отчёты по часам

GetReports возвращает в reports редактируемый встроенный отчёт «Учёт времени · <название поля>» для каждого поля типа HOURS. Его DSL суммирует часы по исполнителям и считает карточки с заполненным значением:

select
  [Исполнитель],
  sum([Часы разработки]) as [Часы],
  count([Часы разработки]) as [Карточки]
from Карточки
where [Часы разработки] is not empty
group by all
having [Часы] > 0
order by [Часы] desc
limit 100

Стабильный UUID поля хранится в queryReferences, поэтому переименование поля не ломает сохранённый отчёт. Архивные карточки не учитываются.

Разрез, агрегаты и фильтры меняются непосредственно в queryDsl. Результат отчёта возвращает PreviewReport; универсальные запросы без сохранения выполняет RunQuery. Отдельного hardcoded API для часовых, потоковых или sprint-метрик нет.

curl --fail-with-body \
  -X POST http://localhost:8080/algoboard.v1.BoardService/GetReports \
  -H "Authorization: Bearer ${ALGOBOARD_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"PROJECT_UUID"}'

Сокращённый ответ:

{
  "reports": [
    {
      "id": "REPORT_UUID",
      "name": "Учёт времени · Часы разработки",
      "visualization": "bar",
      "queryDsl": "select …",
      "systemKey": "hours:FIELD_UUID"
    }
  ]
}

Карточки

Метод Scope Назначение
CreateIssue write Создать карточку с полями, метками и связями
UpdateIssue write Изменить значения карточки
MoveIssue write Переместить карточку и выполнить workflow/automation
MoveTimelineIssue write Изменить порядок карточки на временной шкале
CaptureTimelineBaseline write Зафиксировать текущие даты как базовый план
ArchiveIssue write Архивировать или восстановить
AddComment write Добавить комментарий от владельца токена

Типы карточки: 1 — task, 2 — story, 3 — bug. Приоритеты: 1 — low, 2 — normal, 3 — high, 4 — critical. Допустимые story points: 0, 1, 2, 3, 5, 8, 13.

curl --fail-with-body \
  -X POST http://localhost:8080/algoboard.v1.BoardService/CreateIssue \
  -H "Authorization: Bearer ${ALGOBOARD_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "PROJECT_UUID",
    "columnId": "COLUMN_UUID",
    "title": "Проверить GitLab webhook",
    "description": "Создано агентом после аудита интеграции",
    "type": 1,
    "priority": 3,
    "storyPoints": 3,
    "dependencyIds": [],
    "customFieldValues": [
      {"fieldId": "FIELD_UUID", "value": "Высокая"}
    ]
  }'

Чтобы очистить optional-связь, передайте пустую строку:

{"issueId":"ISSUE_UUID","assigneeId":"","sprintId":""}

MoveIssue проверяет workflow и выполняет automation rules в одной транзакции. Действие MOVE_COLUMN может изменить итоговую колонку.

dependencyIds содержит карточки, которых ждёт текущая карточка. Если ALGO-10.dependencyIds = [ALGO-6], то ALGO-6 блокирует ALGO-10. Для полной замены связей:

{
  "issueId": "ISSUE_UUID",
  "dependencyIds": ["BLOCKING_ISSUE_UUID"],
  "replaceDependencies": true
}

Пустой список с replaceDependencies: true удаляет все зависимости текущей карточки. Сервер запрещает ссылку на саму карточку, связь с другим проектом и любой цикл.

Для календарного планирования используйте timelineDependencies: TimelineDependencyInput[] и replaceTimelineDependencies. В каждой связи задаются predecessorIssueId, type и lagDays: положительный лаг откладывает управляемую границу на рабочие дни, отрицательный задаёт опережение. Старый dependencyIds остаётся совместимым сокращением для связи «окончание → начало» без лага.

Доски и под-доски

Метод Scope Назначение
CreateSavedBoard write Создать фильтрованное представление
UpdateSavedBoard write + ACL Изменить содержимое или управление представлением
DeleteSavedBoard write + ACL Удалить несвязанное представление, сохранив карточки и остальные определения
CreateAccessGroup admin Создать пользовательскую группу участников
UpdateAccessGroup admin Изменить состав, оформление или порядок группы
DeleteAccessGroup admin Удалить группу, если она не используется
CreateProjectRole admin Создать пустую или явно настроенную роль проекта
UpdateProjectRole admin Изменить название, оформление, порядок или разрешения роли
DeleteProjectRole admin Удалить роль, которая не назначена участникам или группам
SetMemberProjectRoles admin Заменить прямые роли участника в текущем проекте
UpsertBoardSubscription ACL view Создать или сохранить личную подписку
DeleteBoardSubscription ACL view Удалить личную подписку
MarkBoardSubscriptionRead ACL view Отметить её события прочитанными
MarkNotificationsRead project.view Отметить персональные уведомления прочитанными
CreateBoardFilterCard ACL view / edit Добавить личный или общий быстрый фильтр на доску
UpdateBoardFilterCard владелец / ACL edit Изменить условия, название, цвет или позицию
DeleteBoardFilterCard владелец / ACL edit Удалить личный или общий фильтр

Карточки принадлежат проекту, а не доске. Доски показывают один набор карточек через разные фильтры. parentId создаёт под-доску; максимальная глубина — восемь уровней.

Общие фильтры доски возвращаются в projectState.boardFilterCards и видны всем участникам с доступом к доске. При personal = true сервер связывает фильтр с текущим участником: такой фильтр выбирается из БД и возвращается только владельцу. Для личного фильтра достаточно ACL view, для общего нужен ACL edit. position в UpdateBoardFilterCard — индекс с нуля внутри своей области: личной конкретного владельца либо общей.

У каждого представления есть обязательный fallback defaultAccess и упорядоченный массив accessRules. Первое совпавшее правило для участника или пользовательской группы определяет none, view, edit либо manage; если совпадений нет, действует fallback. Владелец workspace получает стартовый manage только при отсутствии совпавшего правила: явное правило участника или группы может назначить ему любой уровень, включая none. currentAccess вычисляется сервером, а представления с уровнем none отсутствуют в снимке проекта. Пока возможность access выключена, сохранённые правила не применяются и не удаляются: уровень определяется разрешениями проекта (views.manage, issue.edit, issue.view).

Подписки не получают готового набора событий. Пользователь выбирает фактические ActivityEvent.kind или вводит собственные валидные ключи. Событие попадает в подписку, только если карточка проходит полный фильтр сохранённой доски: быстрые условия и queryDsl применяются совместно по AND. Выключение subscriptionsEnabled приостанавливает доставку, но не удаляет BoardSubscription. Возможность проекта subscriptions управляется отдельно: в выключенном состоянии она сохраняет определения, подавляет уведомления и блокирует изменение или отметку прочтения, но разрешает явное удаление подписки.

{
  "projectId": "PROJECT_UUID",
  "parentId": "BOARD_UUID",
  "name": "Backend · критичные",
  "description": "Критичные задачи backend-команды",
  "color": "violet",
  "defaultAccess": "view",
  "accessRules": [],
  "subscriptionsEnabled": false,
  "filter": {
    "rules": [
      {"field": "labels", "operator": 1, "values": ["BACKEND_LABEL_UUID"]},
      {"field": "priority", "operator": 1, "values": ["4"]},
      {"field": "completed", "operator": 1, "values": ["false"]}
    ],
    "visibleColumnIds": ["TODO_COLUMN_UUID", "DOING_COLUMN_UUID"],
    "cardFieldKeys": ["key", "priority", "due_date", "assignee", "custom:FIELD_UUID"],
    "cardFieldsConfigured": true,
    "includeCompleted": true
  }
}
{
  "boardId": "BOARD_UUID",
  "name": "Мои просроченные",
  "color": "rose",
  "filter": {
    "rules": [
      {"field": "assignee", "operator": 1, "values": ["MEMBER_UUID"]},
      {"field": "overdue", "operator": 1, "values": ["true"]}
    ],
    "visibleColumnIds": ["TODO_COLUMN_UUID", "DOING_COLUMN_UUID", "REVIEW_COLUMN_UUID"],
    "cardFieldKeys": ["key", "priority", "due_date", "labels", "assignee"],
    "cardFieldsConfigured": true,
    "includeCompleted": true
  }
}

Все filter.rules соединяются по AND. Несколько values одного правила означают совпадение хотя бы с одним значением.

BoardFilterOperator Значение
EQUALS 1
NOT_EQUALS 2
CONTAINS 3
NOT_CONTAINS 4
GREATER_THAN 5
LESS_THAN 6
IS_EMPTY 7
IS_NOT_EMPTY 8

Системные ключи поля: search, key, title, description, object_type, type, priority, assignee, reporter, labels, sprint, column, story_points, start_date, due_date, overdue, blocked, completed, parent, dependencies, created_at, updated_at. Кастомное поле задаётся ключом custom:FIELD_UUID, а настраиваемый тип связи — relation:RELATION_TYPE_UUID.

visibleColumnIds задаёт колонки вида. cardFieldKeys задаёт поля карточки; название показывается всегда. Чтобы сохранить явный, в том числе пустой, список полей, передайте cardFieldsConfigured: true. Поля query, assigneeIds, labelIds, priorities, sprintId, onlyUnassigned, onlyOverdue и includeCompleted поддерживаются для ранее созданных досок.

Спринты и администрирование

Группа Методы
Спринты (write) CreateSprint, UpdateSprint, StartSprint, CompleteSprint
Проекты (admin) CreateProject, UpdateProject
Команда (admin) CreateMember, UpdateMember, SetMemberProjectRoles, SetMemberTemporaryPassword, UpdateWorkspace
Стили (admin) CreateColorStyle, UpdateColorStyle, DeleteColorStyle
Процесс (admin) CreateColumn, UpdateColumn, DeleteColumn, CreateWorkflowRule, UpdateWorkflowRule, DeleteWorkflowRule
Типы объектов (admin) CreateObjectType, UpdateObjectType, DeleteObjectType, UpsertObjectTypeField, DeleteObjectTypeField
Типы связей (admin) CreateRelationType, UpdateRelationType, DeleteRelationType
Связи объектов (write) CreateObjectRelation, DeleteObjectRelation
Метки (admin) CreateLabel, UpdateLabel, DeleteLabel
Кастомные поля (admin) CreateCustomField, UpdateCustomField, DeleteCustomField

DeleteColumn требует целевую колонку того же проекта в moveIssuesToColumnId. Метод:

  • переносит активные и архивные карточки;
  • синхронизирует completedAt;
  • переносит ссылки во всех сохранённых фильтрах, политиках, сигналах, бизнес-действиях, расчётах, пакетах, маршрутизации и дашбордах;
  • переносит статические действия по смене состояния и ссылки автоматизаций;
  • блокируется, пока состояние участвует в workflow-переходе: переход не удаляется и не переписывается автоматически;
  • нормализует порядок колонок;
  • возвращает movedIssueCount.

Автоматизации с условием по удалённой колонке перенаправляются на целевую и отключаются. Последнюю колонку проекта удалить нельзя.

{
  "columnId": "REVIEW_COLUMN_UUID",
  "moveIssuesToColumnId": "DOING_COLUMN_UUID"
}

DeleteLabel снимает метку только с карточек. Если метку использует фильтр, автоматизация, бизнес-действие, пакет, маршрутизация или дашборд, сервер блокирует удаление и сохраняет все определения без изменений. Ответ содержит affectedIssueCount; карточки и доски не удаляются.

{"labelId":"OBSOLETE_LABEL_UUID"}

Через пользовательскую сессию участник может изменить свои name, avatarData, avatarContentType и clearAvatar. Для изменения чужого профиля, базовой роли или активности нужны контекст проекта и разрешение team.manage. Любая операция над владельцем либо назначение базовой роли Owner дополнительно требуют workspace.owners.manage. Для агентского токена также нужен scope admin.

CreateMember атомарно создаёт участника и учётную запись. Нужны projectId текущего проекта и разрешение team.manage; создание владельца дополнительно требует workspace.owners.manage.

SetMemberTemporaryPassword создаёт учётную запись для участника без доступа. Если email уже принадлежит существующей учётной записи, метод только связывает с ней участника и не меняет логин, пароль или активные сессии. Нужны projectId и team.manage; для владельца также требуется workspace.owners.manage. Пользователь с новой учётной записью входит по логину или email, затем меняет временный пароль через ChangePassword.

Настраиваемые роли относятся к конкретному проекту и не совпадают с базовым MemberRole. SetMemberProjectRoles заменяет прямые назначения участника. Итоговые разрешения — объединение прямых ролей и ролей всех групп доступа, в которые входит участник.

  • login: 3–32 латинских символа, цифры, точки, дефисы или подчёркивания; регистр не учитывается;
  • password: 8–72 символа; сервер сохраняет bcrypt-хеш.
{
  "workspaceId": "WORKSPACE_UUID",
  "name": "Анна Волкова",
  "email": "anna@company.ru",
  "login": "anna.volkova",
  "password": "temporary-pass-42",
  "role": "MEMBER_ROLE_EDITOR",
  "avatarColor": "sky"
}

Дата передаётся как YYYY-MM-DD. Значение custom field всегда передаётся строкой:

CustomFieldType Значение Формат CustomFieldInput.value
TEXT 1 произвольный текст
NUMBER 2 число в строковом виде
DATE 3 YYYY-MM-DD
SELECT 4 один из options
CHECKBOX 5 "true" или "false"
MEMBER 6 UUID участника того же рабочего пространства
WIKI_ARTICLE 7 UUID статьи из ProjectState.wikiArticles того же проекта
HOURS 8 число от 0 до 100000 в строковом виде, например "2.5"

Для MEMBER используйте ID из GetAppState.members, для WIKI_ARTICLE — из GetAppState.projectState.wikiArticles. Имя, email и ID из другого пространства или проекта не принимаются.

Wiki

Метод Scope Назначение
CreateWikiArticle write Создать корневую статью или подраздел
UpdateWikiArticle write Изменить название, текст, родителя или позицию
DeleteWikiArticle write Удалить статью и всё её поддерево

Статья может содержать текст и дочерние статьи. Глубина не ограничена; циклы и родители из другого проекта запрещены. Пустой parentId переносит статью в корень.

{
  "projectId": "PROJECT_UUID",
  "parentId": "PARENT_ARTICLE_UUID",
  "title": "Откат релиза",
  "body": "# Когда откатывать\n- Ошибка миграции\n- Рост 5xx"
}

DeleteWikiArticleResponse возвращает deletedArticleCount и clearedFieldValueCount. При удалении ветки сервер очищает ссылки полей WIKI_ARTICLE.

Аватары участников

CreateMember и UpdateMember принимают изображение в avatarData и MIME-тип в avatarContentType. В protobuf JSON бинарные данные кодируются в Base64. clearAvatar: true удаляет изображение.

{
  "memberId": "MEMBER_UUID",
  "avatarData": "BASE64_PNG",
  "avatarContentType": "image/png"
}

API принимает PNG или JPEG до 512 КБ и 128×128 пикселей. Интерфейс уменьшает изображение до 128×128 и кодирует его в PNG. Сервер повторно проверяет размер и формат.

Автоматизации

Метод Scope
CreateAutomationRule admin
UpdateAutomationRule admin
DeleteAutomationRule admin
TestAutomationRule admin

Триггеры:

  • 1 — карточка перемещена;
  • 2 — карточка создана;
  • 3 — карточка обновлена; eventFilter.changedFields может ограничить запуск фактически изменившимися полями;
  • 4 — GitLab merge request;
  • 5 — pipeline;
  • 6 — job;
  • 7 — push;
  • 8 — tag push;
  • 9 — deployment;
  • 10 — release;
  • 11 — note/comment.

Действия:

Значение Действие targetId value
1 Переместить в колонку column ID
2 Назначить исполнителя member ID или пусто
3 Установить приоритет 14
4 Установить спринт sprint ID или пусто
5 Добавить метку label ID
6 Записать custom field field ID строковое значение по типу поля, для MEMBER — member ID
7 Применить фильтр доски board ID
8 Задать причину блокировки текст
9 Добавить комментарий текст
10 Изменить поле карточки или сотрудника см. fieldMutation

Для ISSUE_UPDATED пустой changedFields означает любое изменение. Непустой список использует семантику OR: правило запускается, если изменилось хотя бы одно выбранное поле. Системные ключи: title, description, object_type, type, priority, story_points, assignee, sprint, start_date, due_date, parent, blocked_reason, labels, dependencies, checklists. Пользовательское поле записывается как custom:<FIELD_UUID>.

Действие 10 использует типизированный fieldMutation: SET записывает результат формулы, а INC и DEC прибавляют или вычитают числовой результат. Отдельной операции SETINT нет. Формула исполняется тем же серверным lexer, AST, type checker и evaluator, который обслуживает вычисляемые поля, условия и запросы. Действия правила видят результаты предыдущих действий и применяются в одной транзакции; ошибка откатывает изменения карточки и полей персонала.

Записывать можно системные поля карточки title, description, story_points, start_date, due_date, priority, blocked_reason, assignee, sprint; custom fields типов TEXT, NUMBER, HOURS, DATE, CHECKBOX, SELECT, MEMBER, WIKI_ARTICLE; а также настраиваемые поля персонала сотрудника из assignee или конкретного custom field типа MEMBER. INC и DEC разрешены только для NUMBER, HOURS, числовых полей персонала и story_points; пустое текущее числовое значение считается нулём. Вычисляемые поля можно читать в правой части, но нельзя изменять. Колонка, тип объекта, метки, связи, комментарии и чек-листы не являются целями fieldMutation, потому что сохраняют отдельную семантику действий. Старые SET_PRIORITY, SET_SPRINT, SET_CUSTOM_FIELD и SET_BLOCKED_REASON продолжают читаться для совместимости.

GitLab-действия 2035 создают ветки и merge requests, меняют/связывают/одобряют/закрывают/мержат MR, добавляют notes, запускают/повторяют/отменяют pipelines и jobs. Их параметры хранятся в редактируемом AutomationAction.config; строки поддерживают переменные ${issue.key}, ${issue.title}, ${slug.issue_key}, ${event.merge_request_iid}, ${event.pipeline_id}, ${event.job_id} и ${event.ref}.

Правило содержит до 20 действий. Для триггера перемещения fromColumnId и toColumnId опциональны: отсутствие значения означает любую колонку. GitLab-правило явно выбирает integrationId; входящее событие дополнительно проверяется через eventFilter.

Шаблоны создания

Метод Scope Назначение
CreateIssueTemplate admin Создать форму и отдельную отзывную ссылку
UpdateIssueTemplate admin Изменить все свойства формы и порядок полей
DeleteIssueTemplate admin Отозвать форму, сохранив журнал отправок
RotateIssueTemplateLink admin Немедленно заменить секрет ссылки
ResolveIssueTemplate публичный или member Получить безопасную схему формы по token
SubmitIssueTemplate публичный или member Идемпотентно создать карточку через форму

Новый проект не получает форм автоматически. Пользователь с templates.manage явно задаёт access, начальный тип/состояние, срок, лимит отправок, текст успеха и для каждого поля режим hidden/editable/required/locked. Ссылка имеет вид /new?template=<token>; зашифрованный token не возвращается отдельно.

Режимы доступа:

Режим Проверка
PUBLIC Форма открывается без сессии
PASSWORD Сначала возвращается только challenge без полей; пароль проверяется bcrypt-хешем
MEMBERS Требуется активная сессия участника workspace
GROUPS Требуется сессия и членство хотя бы в одной группе из accessGroupIds

Рабочее время и сигналы

Метод Scope Назначение
CreateBusinessCalendar admin Создать календарь с пользовательским расписанием
UpdateBusinessCalendar admin Изменить расписание, исключения, часовой пояс или порядок
DeleteBusinessCalendar admin Удалить календарь, если его не использует политика или сотрудник
CreateTimePolicy admin Создать расчёт рабочего времени по выбранному полю
UpdateTimePolicy admin Изменить условия, длительность или календарь
DeleteTimePolicy admin Удалить политику, если её не использует правило сигнала
CreateSignalRule admin Создать настраиваемое правило отклонения
UpdateSignalRule admin Изменить условия, цвет, политику или порядок
DeleteSignalRule admin Удалить правило без удаления карточек
UpdateSignalState write Подтвердить активный сигнал или вернуть его в работу

Новый проект не получает календарей, политик или правил. Фильтры применения и остановки политики времени, а также фильтр сигнала поддерживают быстрые условия и queryDsl; если заполнены оба режима, сервер соединяет их по AND. Пустой фильтр применения охватывает все карточки, а пустой фильтр остановки не останавливает расчёт. Выключение возможностей time_policies и signals останавливает вычисление, но сохраняет конфигурацию. Деловая семантика сигнала задаётся пользователем через название, описание, цвет и фильтр; фиксированные статусы политики описывают только результат вычисления времени.

Персонал

Метод Scope Назначение
CreatePersonnelField admin Создать настраиваемое поле сотрудника
UpdatePersonnelField admin Изменить имя, тип, варианты или порядок поля
DeletePersonnelField admin Удалить поле и значения, если оно не используется формулой
UpdateMemberProjectProfile admin Назначить рабочий календарь и изменить значения сотрудника в проекте

Новый проект не получает полей персонала и календарных назначений автоматически. Поля относятся к проекту; один участник workspace может иметь разные значения и календарь в разных проектах. MEMBER-поле карточки раскрывает значения в формуле через стабильный источник member_field:<CARD_FIELD_ID>:<PERSONNEL_FIELD_ID> и читаемую подпись вроде [Разработчик.Ставка].

Бизнес-действия

Метод Scope Назначение
CreateBusinessAction admin Создать контекстную команду с пользовательской формой
UpdateBusinessAction admin Изменить применимость, поля формы, операции или порядок
DeleteBusinessAction admin Удалить определение, сохранив журнал выполнений
ExecuteBusinessAction write Атомарно применить настроенную команду к карточке

Новый проект не получает действий и полей формы. Применимость действия задаётся полным BoardFilter: быстрые условия и queryDsl объединяются по AND. Один контракт применимости используется при показе действия у карточки, подсчёте кандидатов во вкладке «Действия» и при окончательной серверной проверке перед атомарным выполнением. Возможность actions включается независимо; её выключение блокирует выполнение, не удаляя определения и журнал.

Вычисляемые поля

Метод Scope Назначение
CreateCalculationField admin Создать формулу или агрегат по настраиваемой связи
UpdateCalculationField admin Изменить источники, область, форматирование, условия или порядок
DeleteCalculationField admin Удалить определение, если на него ничего не ссылается
CreateCalculationConstant admin Создать числовую или строковую константу проекта
UpdateCalculationConstant admin Изменить значение, название, тип или порядок константы
DeleteCalculationConstant admin Удалить константу, если она не используется формулой

Значения не материализуются в карточках: GetAppState вычисляет их из текущих данных. Определения констант возвращаются в ProjectState.calculationConstants. Формулы записываются читаемыми выражениями со ссылками [Название поля]; стабильные CalculationFormulaReference связывают видимую подпись с источником, поэтому переименование не ломает вычисление. Для поля карточки типа MEMBER доступны пути [Поле сотрудника.Свойство персонала], например [Разработчик.Ставка]. Поддерживаются арифметика, скобки, сравнения, логические операции и функции IF, COALESCE, ROUND, MIN, MAX, ABS, CONCAT, DAYS_BETWEEN, DATE_ADD, AND, OR, NOT, LOWER, UPPER, LEN. Тип результата формулы выводится сервером. В rollup-поле relatedFilter поддерживает быстрые условия и queryDsl; сервер применяет их по AND до агрегации связанных карточек.

Новый проект не получает расчётов или констант. Возможность calculations можно выключить без удаления определений; в выключенном состоянии сервер возвращает пустой список calculationValues.

Управляемые пакеты

Метод Scope Назначение
CreateWorkBatch admin Создать пакет с явно выбранным составом, режимом и операциями
UpdateWorkBatch admin Изменить определение без запуска
DeleteWorkBatch admin Удалить определение, сохранив журнал
PreviewWorkBatch write Проверить текущий состав и изменения по каждой карточке
ExecuteWorkBatch write Выполнить только неизменившийся preview

Новый проект не получает пакетов. selectionMode выбирается между динамическим фильтром и зафиксированным списком, executionMode — между атомарным запуском и независимой обработкой карточек. Динамический состав поддерживает queryDsl и быстрые правила rules: сервер применяет их совместно по AND и заново вычисляет состав при каждом preview и выполнении. Пустые DSL и правила выбирают все доступные карточки в пределах maxItems. Сервер не задаёт режимы, цвет, лимит, фильтр или операции. Выключение возможности batches блокирует preview и выполнение, но сохраняет определения и журнал.

Маршрутизация

Метод Scope Назначение
CreateRoutingPolicy admin Создать полностью заданное правило распределения
UpdateRoutingPolicy admin Изменить триггеры, условия, кандидатов, ёмкость и порядок
DeleteRoutingPolicy admin Удалить определение, сохранив журнал решений
PreviewRoutingPolicy write Рассчитать решение и нагрузку кандидатов без изменений
ExecuteRoutingPolicy write Применить неизменившийся ручной preview

Новый проект не получает правил. Команда явно выбирает ручной запуск и/или триггеры, отдельные фильтры применимости и нагрузки, стратегию, упорядоченных кандидатов, вес и ёмкость каждого участника, резервное решение, остановку контура и реакцию на отказ. Оба фильтра поддерживают быстрые условия и queryDsl, соединённые по AND: первый решает, применимо ли правило к карточке, второй определяет активную нагрузку каждого кандидата. Возможность routing включается независимо: её выключение подавляет автоматические запуски и блокирует ручные, но не меняет определения и журнал.

Операционные дашборды

Метод Scope Назначение
CreateDashboard admin Создать дашборд из полностью заданных виджетов
UpdateDashboard admin Изменить область, виджеты, обновление и порядок
DeleteDashboard admin Удалить только определение дашборда
PreviewDashboard read Вычислить текущие значения и строки без изменений данных

Новый проект не получает дашбордов, KPI или виджетов. Команда явно задаёт общий фильтр, а для каждого виджета — название, цвет, визуализацию, собственный фильтр, меру, агрегацию, группировку, период, поле даты, порядок, лимит, табличные поля и ширину. Общая выборка и фильтр виджета поддерживают одновременно визуальные условия и типизированный Query DSL; если заполнены оба режима, они объединяются через AND. Мерами, группировками, датами и колонками таблицы могут быть поля профиля исполнителя или сотрудника из любого карточного поля типа MEMBER: member_field:assignee:<personnelFieldId> и member_field:<memberCustomFieldId>:<personnelFieldId>. Возможность dashboards включается независимо: выключение блокирует вычисление и скрывает рабочий экран, но сохраняет определения.

Интеграции

Метод Scope Назначение
CreateIntegration admin Сохранить зашифрованные реквизиты Jira, YouGile или GitLab
UpdateIntegration admin Изменить конфигурацию; пустой token сохраняет старый
DeleteIntegration admin Удалить подключение и внешние ссылки
TestIntegration admin Проверить токен и доступ к проекту
RunImport admin Импортировать Jira/YouGile или выполнить dryRun
SyncIntegration admin Связать GitLab merge requests с карточками

Provider: 1 — Jira, 2 — YouGile, 3 — GitLab.

Jira использует email + API token и JQL. YouGile использует Bearer token и ID доски. GitLab использует personal/project access token и ID или namespace/project.

{
  "integrationId": "INTEGRATION_UUID",
  "targetBoardId": "BOARD_UUID",
  "targetColumnId": "COLUMN_UUID",
  "dryRun": true,
  "limit": 200
}

GitLab sync ищет ключ карточки (TEAM-42) в названии, описании и source branch merge request. Webhook URL возвращается в integration.webhookUrl; secret передаётся в X-Gitlab-Token.

Агенты и токены

Метод Scope
ListAgents пользователь с api.manage
CreateAgent пользователь с api.manage
UpdateAgent пользователь с api.manage
ListAgentTokens admin
CreateAgentToken admin
RevokeAgentToken admin
ListAgentApprovalReceipts пользователь с api.manage
ReviewAgentApprovalReceipt пользователь с api.manage
ListAgentRuns пользователь с api.manage
ListManagedSkills / ListManagedSkillVersions / CreateManagedSkill / UploadManagedSkillVersion пользователь с api.manage
UpsertAgentSkillAssignment / DeleteAgentSkillAssignment пользователь с api.manage
InvokeAgentSkill пользователь с chat.send
ListRunnableAgentSkillInvocations / DownloadManagedSkillVersion назначенный Agent, read
ClaimAgentSkillInvocation / HeartbeatAgentSkillInvocation / FinishAgentSkillInvocation назначенный Agent, write
ListAgentConnectors / CreateAgentConnectorPairing пользователь с api.manage
ExchangeAgentConnectorPairing публичный одноразовый pairing code
GetAgentConnector / HeartbeatAgentConnector / DisconnectAgentConnector токен именно этого connector либо пользователь с api.manage
{
  "workspaceId": "WORKSPACE_UUID",
  "projectId": "PROJECT_UUID",
  "name": "release-agent",
  "projectRoleIds": ["ROLE_UUID"],
  "capabilities": [
    {"capability": "read", "decision": "allow"},
    {"capability": "write", "decision": "ask"},
    {"capability": "admin", "decision": "deny"}
  ],
  "tokenScopes": ["read", "write"],
  "tokenExpiresInDays": 30
}

projectId задаёт контекст проверки api.manage и назначения ролей. Агент получает отдельный memberId; токены ссылаются на один agentId, поэтому ротация секрета не меняет авторство. CreateAgentResponse.secret и CreateAgentTokenResponse.secret возвращаются один раз. Сервер хранит SHA-256 hash.

Policy ask создаёт receipt только после успешной проверки scope, роли и разрешения проекта. Pending receipt живёт 15 минут. Approve сам не запускает команду: Agent повторяет точный protobuf-запрос с заголовком X-Algoboard-Approval. Receipt связан с procedure, Agent, token, configVersion, полным request hash и snapshot состояния; он атомарно используется не более одного раза. Request preview хранится с маскированием секретов.

Каждый аутентифицированный Agent RPC дополнительно получает X-Algoboard-Agent-Run-Id. Запись execution ledger создаётся до проверки capability/RBAC и поэтому сохраняет как разрешённые действия, так и denied, failed и approval_required. Клиент может передать диагностическую метку в X-Algoboard-Agent-Client; security identity всё равно определяется только Agent token.

Машиночитаемая точка входа

GET /.well-known/algoboard-agent.json

Ответ содержит Base URL, scopes, команду MCP и список инструментов. Руководство агента доступно по GET /docs/agent-guide.md.

Полный RPC-справочник

Путь unary RPC:

POST /algoboard.v1.BoardService/{Method}

В сигнатурах ниже ? означает optional-поле: если его не передать, сервер сохраняет текущее значение. [] означает массив. Названия приведены в protobuf JSON — lowerCamelCase.

Обозначения доступа:

  • public — токен не нужен;
  • optional — без токена возвращается гостевое состояние, с токеном — текущая сессия;
  • read — браузерная сессия или agent token scope read; для проектных RPC дополнительно проверяется указанное у метода разрешение;
  • write — agent token scope write и соответствующее разрешение проекта; для board-scoped методов дополнительно и приоритетно действует ACL самого представления;
  • admin / owner — историческая метка раздела: agent token требует scope admin, а пользователь — соответствующее атомарное разрешение проекта; базовая роль Owner сама по себе не обходит явно назначенные проектные роли;
  • self / admin — участник может менять ограниченный набор собственных полей, для административного изменения нужны team.manage, а для владельцев ещё и workspace.owners.manage.

Сессия, состояние и отчёты

Register

  • Доступ: public
  • Контракт: RegisterRequest → RegisterResponse
  • Request: {name:string, email:string, password:string, workspaceName:string, projectName:string, projectKey:string}
  • Response: {session:AuthSession}

Создаёт workspace, пустой проект с указанными именем и ключом, учётную запись владельца, участника с ролью owner и браузерную cookie-сессию. Типы объектов, колонки и остальные элементы процесса пользователь настраивает самостоятельно.

Login

  • Доступ: public
  • Контракт: LoginRequest → LoginResponse
  • Request: {email:string, password:string}
  • Response: {session:AuthSession}

Поле email принимает email или уникальный login без учёта регистра. Успешный ответ устанавливает cookie algoboard_session.

GetSession

  • Доступ: optional
  • Контракт: GetSessionRequest → GetSessionResponse
  • Request: {}
  • Response: {session:AuthSession}

Без корректной cookie или Bearer-токена возвращает session.authenticated = false, а не ошибку unauthenticated.

Logout

  • Доступ: read
  • Контракт: LogoutRequest → LogoutResponse
  • Request: {}
  • Response: {}

Удаляет текущую браузерную сессию. Для агентского токена операция не отзывает token; используйте RevokeAgentToken.

ChangePassword

  • Доступ: self
  • Контракт: ChangePasswordRequest → ChangePasswordResponse
  • Request: {currentPassword:string, newPassword:string}
  • Response: {}

Проверяет текущий пароль вошедшего пользователя и заменяет его bcrypt-хеш. newPassword должен содержать 8–72 символа и отличаться от currentPassword. Текущая сессия сохраняется.

AdminListTenants

  • Доступ: superadmin
  • Контракт: AdminListTenantsRequest → AdminListTenantsResponse
  • Request: {}
  • Response: {tenants:AdminTenantSummary[]}

Возвращает все рабочие пространства. AdminTenantSummary содержит workspace, peopleCount, activePeopleCount, accountCount и projectCount.

AdminListTenantPeople

  • Доступ: superadmin
  • Контракт: AdminListTenantPeopleRequest → AdminListTenantPeopleResponse
  • Request: {workspaceId:string}
  • Response: {people:AdminTenantPerson[]}

Для каждого человека AdminTenantPerson возвращает member, hasAccount, accountId, accountLogin, lastLoginAt и tenantCount. Последнее поле показывает, в скольких тенантах используется единая учётная запись.

AdminResetTenantMemberPassword

  • Доступ: superadmin
  • Контракт: AdminResetTenantMemberPasswordRequest → AdminResetTenantMemberPasswordResponse
  • Request: {workspaceId:string, memberId:string, newPassword:string}
  • Response: {person:AdminTenantPerson}

Устанавливает новый bcrypt-пароль и отзывает все активные сессии целевой учётной записи. Если у человека ещё нет аккаунта, создаёт его и связывает со всеми участниками с тем же email. Сам пароль не попадает в ответ или audit log; журнал сохраняет автора, тенант, участника, аккаунт и время операции. Для аккаунта, связанного с несколькими тенантами, пароль меняется глобально.

GetAppState

  • Доступ: read
  • Контракт: GetAppStateRequest → GetAppStateResponse
  • Request: {projectId?:string, view:string}
  • Response: {workspace:Workspace, members:Member[], projects:Project[], projectState:ProjectState, currentMemberId:string, colorStyles:ColorStyle[]}

Если projectId не передан, сервер выбирает первый активный доступный проект. Это основной вызов для получения UUID перед изменяющими командами. Необязательный view принимает имя экрана (overview, board, signals, dashboards, reports, team, wiki, settings и другие значения интерфейса). Сервер не загружает журналы и конфигурацию возможностей, которые этому экрану не нужны. Пустой view сохраняет полный ответ для интеграций.

Список issues содержит облегчённые карточки: без текста комментариев и чек-листов, но с commentCount. Полная карточка запрашивается через GetIssue.

GetIssue

  • Доступ: issue.view
  • Контракт: GetIssueRequest → GetIssueResponse
  • Request: {issueId:string}
  • Response: {issue:Issue}

Возвращает одну доступную карточку целиком: описание, комментарии с авторами, чек-листы, пользовательские значения, зависимости и внешние ссылки. Используйте этот вызов при открытии карточки вместо загрузки подробностей всех карточек через GetAppState.

RunQuery

  • Доступ: read; дополнительно issue.view для from Карточки и from Изменения или team.view для from Сотрудники
  • Контракт: RunQueryRequest → RunQueryResponse
  • Request: {projectId:string, query:string, references:CalculationFormulaReference[], cardFilter:BoardFilter, contextCardId?:string, pageSize:int32, pageToken:string, parameterValues:QueryParameterValue[]}
  • Response: {columns:QueryColumn[], rows:QueryRow[], totalCount:int32, nextPageToken:string, references:CalculationFormulaReference[], normalizedQuery:string, parameters:QueryParameterDefinition[], resolvedContexts:QueryResolvedContext[], explain:QueryExplainStep[]}

Выполняет ограниченный типизированный DSL только на сервере. Каноническая запись использует строчные ключевые слова: [param $name: type = default] select [distinct] … from Карточки|Карточки.MEMBER_поле|Сотрудники|Изменения [where …] [group by …|all] [having …] [order by […] asc|desc [, …]] [limit n]. JOIN, подзапросы, запись данных и произвольные функции запрещены; LIMIT находится в диапазоне 1..500. pageSize ограничивает размер одной страницы, а непрозрачный pageToken привязан к тексту запроса, фильтру и контексту карточки.

Проекции поддерживают as [Понятный заголовок]. Для отчётов доступны group by, group by all, having и агрегаты count(), count_distinct, sum, avg, min, max, условные варианты *_if, median и percentile. Выражение следующего столбца может ссылаться на уже рассчитанный алиас, например percent([Готово], [План]) as [Готовность, %]. Оконные функции previous, change, percent_change, running_sum, moving_avg и rank работают после обязательного order by. explode([Метки]) разворачивает список перед группировкой. group by all автоматически включает все неагрегированные проекции; group by и having могут ссылаться на алиасы выбранных столбцов. date_bucket(дата, "day"|"week"|"month") строит временные ряды. Для заполненности используйте читаемые операторы is empty и is not empty.

WHERE и HAVING не имеют отдельного интерпретатора: формулы вычисляемых полей, условия автоматизаций и запросы используют один lexer, AST, проверку типов и evaluator. Разрешены арифметика, сравнения, and/or/not, in, not in, between, contains, not contains, matches и фиксированный набор функций if, coalesce, round, min, max, abs, concat, days_between, date_add, lower, upper, len, starts_with, ends_with, contains_any, contains_all, date_bucket, last, next, range, shift, start_of, end_of, working_days_between, working_hours_between, add_working_days и is_working_day. Контексты @now, @today, @me, @activeSprint, @previousSprint, @lastWorkingDay, @lastMonth и другие вычисляются заново при каждом запуске в часовом поясе рабочего календаря или проекта. Имена в квадратных скобках сервер связывает со стабильными references; права на скрытые поля проверяются до выполнения.

Параметр объявляется до select, например param $period: period = last(30d). Поддерживаются типы period, date, datetime, member, sprint, text, number и boolean. Значения одного запуска передаются через parameterValues; если значения нет, используется выражение по умолчанию. parameters возвращает разрешённые значения параметров, resolvedContexts — фактические значения @…, а explain — реальные количества строк до и после каждого этапа выполнения.

select Карточка
from Карточки
where [Приоритет] >= 3
  and [Срок] < @today
order by [Срок] asc
limit 100

Сотрудника из конкретного поля карточки выбирают явным типизированным путём:

select Сотрудник
from Карточки.Поле_сотрудника
where [Сотрудник.Ставка] > 1000
limit 100

После точки принимается key или название поля типа MEMBER; для названия с пробелами используйте Карточки.[Поле сотрудника]. Сервер сохраняет UUID поля в references, отклоняет поля других типов и пропускает карточки без выбранного сотрудника. [Сотрудник.Ставка] в таком запросе относится именно к человеку из указанного поля. Без distinct один сотрудник может встретиться в нескольких строках — по одной на каждую карточку. Неявного выбора исполнителя для select Сотрудник from Карточки нет.

Источник Сотрудники возвращает только активных участников проекта и поддерживает системные поля ID, Имя, Email, Логин, Роль, Активен, а также доступные поля персонала:

select Сотрудник
from Сотрудники
where [Сотрудник.Ставка] > 1000
  and [Карточка.Приоритет] >= 3
order by [Сотрудник.Ставка] asc
limit 1

Карточка в запросе from Сотрудники — уже извлечённый скалярный контекст, а не неявный join. Во внешнем API его задаёт contextCardId; в автоматизации сервер передаёт объект карточки события напрямую, без дополнительного поиска. Без контекста обращение к [Карточка.…] отклоняется до выполнения. QueryColumn описывает label, стабильный source и type; каждый QueryValue содержит raw, отображаемый display, type и признак present. QueryRow содержит выбранную card или employee и массив values.

GetReports

  • Доступ: read
  • Контракт: GetReportsRequest → GetReportsResponse
  • Request: {projectId:string}
  • Response: {reports:Report[]}

Report содержит id, projectId, name, description, enabled, color, visualization, queryDsl, queryReferences, systemKey, position, createdAt и updatedAt. Непустой systemKey отличает встроенный отчёт: его разрешено менять и скрывать, но нельзя удалить. Встроенные определения создаются лениво при первом чтении проекта и используют тот же DSL, что пользовательские.

CreateReport

  • Доступ: reports.manage
  • Контракт: CreateReportRequest → CreateReportResponse
  • Request: {projectId:string, name:string, description:string, enabled:bool, color:string, visualization:string, queryDsl:string, queryReferences:CalculationFormulaReference[]}
  • Response: {report:Report}

Создаёт сохранённый отчёт после серверной компиляции DSL. visualization принимает number, table, bar, line или burndown. В проекте может быть до 100 отчётов.

UpdateReport

  • Доступ: reports.manage
  • Контракт: UpdateReportRequest → UpdateReportResponse
  • Request: {reportId:string, name?:string, description?:string, enabled?:bool, color?:string, visualization?:string, queryDsl?:string, queryReferences:CalculationFormulaReference[], replaceQuery:bool, position?:int32}
  • Response: {report:Report}

Редактирует и пользовательские, и встроенные отчёты. Для замены DSL передайте replaceQuery:true, новый queryDsl и полный список queryReferences; сервер повторно компилирует запрос до сохранения.

DeleteReport

  • Доступ: reports.manage
  • Контракт: DeleteReportRequest → DeleteReportResponse
  • Request: {reportId:string}
  • Response: {}

Удаляет только пользовательский отчёт. Встроенный отчёт с systemKey можно отключить через UpdateReport(enabled:false), но нельзя удалить.

PreviewReport

  • Доступ: reports.view
  • Контракт: PreviewReportRequest → PreviewReportResponse
  • Request: {reportId:string, parameterValues:QueryParameterValue[]}
  • Response: {preview:ReportPreview}

ReportPreview содержит reportId, columns, rows, totalCount, references, normalizedQuery, evaluatedAt, parameters, resolvedContexts и explain. Предпросмотр всегда выполняет сохранённый запрос на сервере с текущими правами пользователя и актуальными данными проекта.

GetAgentManifest

  • Доступ: public
  • Контракт: GetAgentManifestRequest → GetAgentManifestResponse
  • Request: {}
  • Response: {manifest:AgentManifest}

Возвращает тот же контракт, что и GET /.well-known/algoboard-agent.json.

Проекты, пространство и команда

CreateProject

  • Доступ: admin / owner
  • Контракт: CreateProjectRequest → CreateProjectResponse
  • Request: {workspaceId:string, name:string, key:string, color:string, projectId:string, memberIds:string[]}
  • Семантика: создатель автоматически становится администратором проекта; активные участники из memberIds получают стандартную рабочую роль. Участники рабочего пространства, которых нет в списке, не получают доступ к новому проекту.
  • Response: {project:Project}

key становится неизменяемым префиксом карточек проекта. projectId — обязательный контекст текущего проекта, по которому проверяется разрешение workspace.projects.create.

UpdateProject

  • Доступ: admin / owner
  • Контракт: UpdateProjectRequest → UpdateProjectResponse
  • Request: {projectId:string, name?:string, color?:string, archived?:boolean}
  • Response: {project:Project}

Архивный проект доступен для чтения, но его рабочие сущности нельзя изменять.

DeleteProject

  • Доступ: admin / owner (project.manage)
  • Контракт: DeleteProjectRequest → DeleteProjectResponse
  • Request: {projectId:string}
  • Response: {}

Необратимо удаляет только архивный проект. Вместе с проектом каскадно удаляются карточки, спринты, настройки, автоматизации, интеграции и остальные связанные данные. Попытка удалить активный проект завершается конфликтом: перед полным удалением проект необходимо архивировать.

UpdateProjectCapability

  • Доступ: admin / owner
  • Контракт: UpdateProjectCapabilityRequest → UpdateProjectCapabilityResponse
  • Request: {projectId:string, key:string, name?:string, description?:string, icon?:string, color?:string, enabled?:boolean, position?:int32}
  • Response: {capability:ProjectCapability}

Настраивает один независимый движок проекта. key выбирается из projectState.capabilities; системный ключ стабилен, а name, description, icon, color и position редактируются владельцем. Выключение через enabled:false скрывает интерфейс и останавливает поведение возможности, но не удаляет её данные и не меняет состояние других возможностей.

ConfigureProjectCapabilities

  • Доступ: admin / owner
  • Контракт: ConfigureProjectCapabilitiesRequest → ConfigureProjectCapabilitiesResponse
  • Request: {projectId:string, enabledKeys:string[]}
  • Response: {capabilities:ProjectCapability[]}

Одним вызовом сохраняет стартовый состав возможностей проекта. Сервер отмечает настроенным весь каталог: ключи из enabledKeys включает, остальные выключает без удаления данных. Неизвестный ключ отклоняет весь запрос.

UpdateWorkspace

  • Доступ: admin / owner
  • Контракт: UpdateWorkspaceRequest → UpdateWorkspaceResponse
  • Request: {workspaceId:string, name?:string, timezone?:string, defaultSprintDays?:int32, projectId?:string}
  • Response: {workspace:Workspace}

timezone использует IANA-name, например Europe/Moscow.

CreateColorStyle

  • Доступ: admin / owner
  • Контракт: CreateColorStyleRequest → CreateColorStyleResponse
  • Request: {workspaceId:string, name:string, value:string, projectId?:string}
  • Response: {colorStyle:ColorStyle}

Добавляет цвет в палитру рабочего пространства. value — шестизначный HEX, например #246BFD. Стабильный внутренний key создаёт сервер; его используют поля color проектов, типов объектов, связей, меток, представлений, общих фильтров и аватаров.

UpdateColorStyle

  • Доступ: admin / owner
  • Контракт: UpdateColorStyleRequest → UpdateColorStyleResponse
  • Request: {colorStyleId:string, name?:string, value?:string, position?:int32, projectId?:string}
  • Response: {colorStyle:ColorStyle}

Изменяет пользовательское название, HEX или порядок цвета. key не меняется, поэтому новый оттенок сразу применяется ко всем существующим назначениям.

DeleteColorStyle

  • Доступ: admin / owner
  • Контракт: DeleteColorStyleRequest → DeleteColorStyleResponse
  • Request: {colorStyleId:string, projectId?:string}
  • Response: {}

Удаляет только неиспользуемый цвет. Перед удалением замените его во всех назначениях; в рабочем пространстве должен остаться хотя бы один цвет.

CreateMember

  • Доступ: admin / owner
  • Контракт: CreateMemberRequest → CreateMemberResponse
  • Request: {workspaceId:string, name:string, email:string, role:MemberRole, avatarColor:string, avatarData?:bytes, avatarContentType?:string, login:string, password:string, projectId:string}
  • Response: {member:Member}

Создаёт участника и учётную запись атомарно. avatarData в JSON — Base64. Ограничения login, password и изображения описаны выше в разделе «Спринты и администрирование».

UpdateMember

  • Доступ: self / admin
  • Контракт: UpdateMemberRequest → UpdateMemberResponse
  • Request: {memberId:string, name?:string, role?:MemberRole, active?:boolean, avatarData?:bytes, avatarContentType?:string, clearAvatar?:boolean, projectId?:string}
  • Response: {member:Member}

Участник может изменить у себя только name и avatar-поля без projectId. Для роли, активности или чужого профиля нужны projectId и team.manage. Операции над владельцем и назначение базовой роли Owner дополнительно требуют workspace.owners.manage. Для agent token всегда нужен scope admin.

SetMemberTemporaryPassword

  • Доступ: admin / owner
  • Контракт: SetMemberTemporaryPasswordRequest → SetMemberTemporaryPasswordResponse
  • Request: {memberId:string, projectId:string, login:string, temporaryPassword:string}
  • Response: {member:Member}

Создаёт учётную запись для импортированного участника без доступа. login должен быть уникальным и содержать 3–32 допустимых символа, temporaryPassword — 8–72 символа. Если email участника уже связан с учётной записью, login и temporaryPassword игнорируются: участник привязывается к существующей записи, а её пароль и сессии не меняются. Метод требует team.manage; для участника-владельца дополнительно проверяется workspace.owners.manage. Свой пароль пользователь меняет через ChangePassword.

CreatePersonnelField

  • Доступ: personnel.manage; для agent token дополнительно scope admin
  • Контракт: CreatePersonnelFieldRequest → CreatePersonnelFieldResponse
  • Request: {projectId:string, name:string, description:string, type:string, options:string[]}
  • Response: {field:PersonnelFieldDefinition}

type принимает text, number, date, boolean или select. Для select нужен хотя бы один пользовательский вариант; для остальных типов options очищается.

UpdatePersonnelField

  • Доступ: personnel.manage; для agent token дополнительно scope admin
  • Контракт: UpdatePersonnelFieldRequest → UpdatePersonnelFieldResponse
  • Request: {fieldId:string, name?:string, description?:string, type?:string, options:string[], replaceOptions:boolean, position?:int32}
  • Response: {field:PersonnelFieldDefinition}

Изменение типа или вариантов отклоняется, если существующее значение сотрудника станет недопустимым.

DeletePersonnelField

  • Доступ: personnel.manage; для agent token дополнительно scope admin
  • Контракт: DeletePersonnelFieldRequest → DeletePersonnelFieldResponse
  • Request: {fieldId:string}
  • Response: {}

Значения удаляются каскадно. Если CalculationFormulaReference использует поле персонала, сервер возвращает conflict и не меняет данные.

UpdateMemberProjectProfile

  • Доступ: personnel.manage; для agent token дополнительно scope admin
  • Контракт: UpdateMemberProjectProfileRequest → UpdateMemberProjectProfileResponse
  • Request: {projectId:string, memberId:string, businessCalendarId?:string, clearBusinessCalendar:boolean, values:PersonnelFieldValue[], replaceValues:boolean}
  • Response: {profile:MemberProjectProfile}

businessCalendarId должен принадлежать тому же проекту. clearBusinessCalendar снимает назначение. replaceValues:true полностью заменяет значения; пустое значение удаляет конкретное свойство. Без replaceValues переданные значения сливаются с сохранёнными.

Карточки и комментарии

CreateIssue

  • Доступ: write
  • Контракт: CreateIssueRequest → CreateIssueResponse
  • Request: {projectId:string, columnId?:string, sprintId?:string, assigneeId?:string, reporterId?:string, title:string, description:string, type:IssueType, priority:IssuePriority, storyPoints:int32, labelIds:string[], startDate?:string, dueDate?:string, progressPercent:int32, actualStartDate?:string, actualFinishDate?:string, milestone:boolean, deadlineDate?:string, parentId?:string, dependencyIds:string[], timelineDependencies:TimelineDependencyInput[], customFieldValues:CustomFieldInput[], checklists:IssueChecklistInput[], blockedReason:string, objectTypeId?:string, chatSource?:ChatIssueSourceInput}
  • Response: {issue:Issue}

Если columnId не передан, сервер использует первую колонку проекта. chatSource связывает новую карточку с доступным пользовательским сообщением: сервер проверяет chat.view и issue.create, атомарно сохраняет неизменяемый снимок исходного content/plain text, пишет аудит и публикует chat.message.updated. Для одного messageId может существовать только одна карточка; повтор того же запроса возвращает уже связанную карточку. Остальные Create-команды пока не поддерживают idempotency key.

UpdateIssue

  • Доступ: write
  • Контракт: UpdateIssueRequest → UpdateIssueResponse
  • Request: {issueId:string, title?:string, description?:string, type?:IssueType, priority?:IssuePriority, storyPoints?:int32, assigneeId?:string, sprintId?:string, labelIds:string[], replaceLabels:boolean, startDate?:string, dueDate?:string, progressPercent?:int32, actualStartDate?:string, actualFinishDate?:string, milestone?:boolean, deadlineDate?:string, parentId?:string, dependencyIds:string[], replaceDependencies:boolean, timelineDependencies:TimelineDependencyInput[], replaceTimelineDependencies:boolean, customFieldValues:CustomFieldInput[], replaceCustomFields:boolean, checklists:IssueChecklistInput[], replaceChecklists:boolean, blockedReason?:string, objectTypeId?:string}
  • Response: {issue:Issue}

Массив изменяет данные только вместе с соответствующим replace... = true. Пустая optional-строка очищает связь или значение. Например, {"issueId":"…","assigneeId":""} снимает исполнителя.

MoveIssue

  • Доступ: write
  • Контракт: MoveIssueRequest → MoveIssueResponse
  • Request: {issueId:string, columnId:string, position:int32}
  • Response: {issue:Issue}

Проверяет workflow rule, WIP и обязательные поля, затем выполняет automation rules в одной транзакции. Перечитайте карточку после ответа: автоматизация могла изменить итоговую колонку или исполнителя.

MoveTimelineIssue

  • Доступ: write
  • Контракт: MoveTimelineIssueRequest → MoveTimelineIssueResponse
  • Request: {issueId:string, targetIssueId:string, placeAfter:boolean}
  • Response: {issue:Issue}

Переставляет карточку на временной шкале относительно targetIssueId. При placeAfter:true карточка ставится после цели, иначе перед ней. Позиция сохраняется в Issue.timelinePosition.

CaptureTimelineBaseline

  • Доступ: write
  • Контракт: CaptureTimelineBaselineRequest → CaptureTimelineBaselineResponse
  • Request: {projectId:string}
  • Response: {issueCount:int32, capturedAt:Timestamp}

Копирует текущие startDate и dueDate всех неархивных карточек проекта в одну пару базовых дат. Повторный вызов заменяет прежний снимок; текущий план не меняется.

ArchiveIssue

  • Доступ: write
  • Контракт: ArchiveIssueRequest → ArchiveIssueResponse
  • Request: {issueId:string, archived:boolean}
  • Response: {issue:Issue}

archived = false восстанавливает карточку.

AddComment

  • Доступ: write
  • Контракт: AddCommentRequest → AddCommentResponse
  • Request: {issueId:string, authorId:string, body:string}
  • Response: {comment:Comment}

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

Спринты

CreateSprint

  • Доступ: write
  • Контракт: CreateSprintRequest → CreateSprintResponse
  • Request: {projectId:string, name:string, goal:string, startDate:string, endDate:string}
  • Response: {sprint:Sprint}

Создаёт спринт в статусе SPRINT_STATUS_PLANNED. Даты передаются как YYYY-MM-DD.

UpdateSprint

  • Доступ: write
  • Контракт: UpdateSprintRequest → UpdateSprintResponse
  • Request: {sprintId:string, name?:string, goal?:string, startDate?:string, endDate?:string}
  • Response: {sprint:Sprint}

StartSprint

  • Доступ: write
  • Контракт: StartSprintRequest → StartSprintResponse
  • Request: {sprintId:string}
  • Response: {sprint:Sprint}

Переводит запланированный спринт в active. В проекте может быть только один активный спринт.

CompleteSprint

  • Доступ: write
  • Контракт: CompleteSprintRequest → CompleteSprintResponse
  • Request: {sprintId:string}
  • Response: {sprint:Sprint}

Завершает активный спринт. Незавершённые карточки перемещаются в первую колонку категории TODO, а их sprintId очищается.

Колонки, метки и workflow

CreateColumn

  • Доступ: admin / owner
  • Контракт: CreateColumnRequest → CreateColumnResponse
  • Request: {projectId:string, name:string, wipLimit:int32, category:ColumnCategory, objectTypeId?:string}
  • Response: {column:BoardColumn}

Создаёт колонку в конце процесса. objectTypeId ограничивает колонку одним типом объекта; пустое значение оставляет её общей.

UpdateColumn

  • Доступ: admin / owner
  • Контракт: UpdateColumnRequest → UpdateColumnResponse
  • Request: {columnId:string, name?:string, wipLimit?:int32, category?:ColumnCategory, position?:int32, objectTypeId?:string}
  • Response: {column:BoardColumn}

wipLimit = 0 означает отсутствие лимита. position меняет порядок, а пустой objectTypeId делает колонку общей.

DeleteColumn

  • Доступ: admin / owner
  • Контракт: DeleteColumnRequest → DeleteColumnResponse
  • Request: {columnId:string, moveIssuesToColumnId:string}
  • Response: {movedIssueCount:int32}

moveIssuesToColumnId обязателен, если в состоянии есть объекты либо его нужно явно заменить в зависимых фильтрах и действиях; целевое состояние должно принадлежать тому же проекту и отличаться от удаляемого. Без цели можно удалить только пустое и ни на что не ссылающееся состояние. Workflow-переходы всегда нужно изменить или удалить отдельно.

CreateLabel

  • Доступ: admin / owner
  • Контракт: CreateLabelRequest → CreateLabelResponse
  • Request: {projectId:string, name:string, color:string}
  • Response: {label:Label}

UpdateLabel

  • Доступ: admin / owner
  • Контракт: UpdateLabelRequest → UpdateLabelResponse
  • Request: {labelId:string, name?:string, color?:string}
  • Response: {label:Label}

DeleteLabel

  • Доступ: admin / owner
  • Контракт: DeleteLabelRequest → DeleteLabelResponse
  • Request: {labelId:string}
  • Response: {affectedIssueCount:int32}

Снимает метку с карточек. Любая ссылка из фильтра, автоматизации, бизнес-действия, пакета, маршрутизации или дашборда блокирует удаление; сервер ничего не переписывает молча.

CreateWorkflowRule

  • Доступ: admin / owner
  • Контракт: CreateWorkflowRuleRequest → CreateWorkflowRuleResponse
  • Request: {projectId:string, fromColumnId:string, toColumnId:string, enabled:boolean, allowedRoles:MemberRole[], allowedProjectRoleIds:string[], requireAssignee:boolean, requireStoryPoints:boolean, requireSprint:boolean, requiredCustomFieldIds:string[], requiredSystemFields:string[]}
  • Response: {rule:WorkflowRule}

Создаёт разрешённый переход между двумя разными колонками одного проекта и его обязательные условия. В проектах с гранулярными правами переход ограничивается через allowedProjectRoleIds; allowedRoles сохранён для совместимости с базовой моделью ролей.

UpdateWorkflowRule

  • Доступ: admin / owner
  • Контракт: UpdateWorkflowRuleRequest → UpdateWorkflowRuleResponse
  • Request: {ruleId:string, enabled?:boolean, allowedRoles:MemberRole[], replaceAllowedRoles:boolean, allowedProjectRoleIds:string[], replaceAllowedProjectRoleIds:boolean, requireAssignee?:boolean, requireStoryPoints?:boolean, requireSprint?:boolean, requiredCustomFieldIds:string[], replaceRequiredCustomFields:boolean, requiredSystemFields:string[], replaceRequiredSystemFields:boolean}
  • Response: {rule:WorkflowRule}

Правило относится к конкретной паре fromColumnId → toColumnId; API обновляет его ограничения, но не меняет пару колонок.

DeleteWorkflowRule

  • Доступ: admin / owner
  • Контракт: DeleteWorkflowRuleRequest → DeleteWorkflowRuleResponse
  • Request: {ruleId:string}
  • Response: {}

Удаляет переход из workflow. Карточки и колонки не изменяются.

Типы объектов и их поля

CreateObjectType

  • Доступ: admin / owner
  • Контракт: CreateObjectTypeRequest → CreateObjectTypeResponse
  • Request: {projectId:string, name:string, pluralName:string, key:string, description:string, icon:string, color:string, titleLabel:string}
  • Response: {objectType:ObjectType}

Создаёт тип карточки проекта. key используется как стабильный идентификатор типа, а titleLabel — как подпись основного поля названия.

UpdateObjectType

  • Доступ: admin / owner
  • Контракт: UpdateObjectTypeRequest → UpdateObjectTypeResponse
  • Request: {objectTypeId:string, name?:string, pluralName?:string, key?:string, description?:string, icon?:string, color?:string, titleLabel?:string, position?:int32, archived?:boolean}
  • Response: {objectType:ObjectType}

Обновляет представление и порядок типа. Архивный тип остаётся доступен существующим карточкам, но не предлагается для новых.

DeleteObjectType

  • Доступ: admin / owner
  • Контракт: DeleteObjectTypeRequest → DeleteObjectTypeResponse
  • Request: {objectTypeId:string}
  • Response: {}

Удаление доступно только когда тип больше не используется карточками, колонками, связями, расчётами, пакетами, маршрутизацией, дашбордами, представлениями или другими условиями.

UpsertObjectTypeField

  • Доступ: admin / owner
  • Контракт: UpsertObjectTypeFieldRequest → UpsertObjectTypeFieldResponse
  • Request: {objectTypeId:string, fieldId:string, label:string, description:string, required:boolean, showOnCard:boolean, visibleOnCreate:boolean, readOnly:boolean, position:int32, defaultValue:string}
  • Response: {field:ObjectTypeField}

Добавляет поле в схему типа или атомарно обновляет его настройки. Само определение пользовательского поля должно уже существовать в проекте.

DeleteObjectTypeField

  • Доступ: admin / owner
  • Контракт: DeleteObjectTypeFieldRequest → DeleteObjectTypeFieldResponse
  • Request: {objectTypeId:string, fieldId:string}
  • Response: {}

Убирает поле из схемы типа, не удаляя глобальное определение и сохранённые значения карточек. Если поле обязательно для workflow-перехода этого типа, удаление блокируется до явного изменения правила.

Типы связей и связи объектов

CreateRelationType

  • Доступ: admin / owner
  • Контракт: CreateRelationTypeRequest → CreateRelationTypeResponse
  • Request: {projectId:string, sourceObjectTypeId:string, targetObjectTypeId:string, name:string, reverseName:string, key:string, cardinality:RelationCardinality, color:string}
  • Response: {relationType:RelationType}

Создаёт редактируемое определение связи между двумя типами объектов. name описывает направление от источника к цели, reverseName — обратное направление, а key служит стабильным ключом для фильтров и интеграций.

UpdateRelationType

  • Доступ: admin / owner
  • Контракт: UpdateRelationTypeRequest → UpdateRelationTypeResponse
  • Request: {relationTypeId:string, sourceObjectTypeId?:string, targetObjectTypeId?:string, name?:string, reverseName?:string, key?:string, cardinality?:RelationCardinality, color?:string, position?:int32, archived?:boolean}
  • Response: {relationType:RelationType}

Позволяет менять оба типа, формулировки направлений, ключ, кардинальность, цвет, порядок и архивный статус. Изменение типов или кардинальности отклоняется, если существующие связи перестанут соответствовать новой схеме.

DeleteRelationType

  • Доступ: admin / owner
  • Контракт: DeleteRelationTypeRequest → DeleteRelationTypeResponse
  • Request: {relationTypeId:string}
  • Response: {}

Удаляет только неиспользуемое определение связи. Если существуют экземпляры, расчёты, пакеты, маршрутизация, дашборды, представления или другие условия со ссылкой на тип, сервер требует сначала изменить их; фильтры не очищаются автоматически.

CreateObjectRelation

  • Доступ: write
  • Контракт: CreateObjectRelationRequest → CreateObjectRelationResponse
  • Request: {relationTypeId:string, sourceIssueId:string, targetIssueId:string}
  • Response: {relation:ObjectRelation}

Связывает два объекта в соответствии с направлением, допустимыми типами и кардинальностью определения. Самоссылки и дубликаты запрещены.

DeleteObjectRelation

  • Доступ: write
  • Контракт: DeleteObjectRelationRequest → DeleteObjectRelationResponse
  • Request: {relationId:string}
  • Response: {}

Удаляет только выбранный экземпляр связи, не меняя её тип.

Кастомные поля

CreateCustomField

  • Доступ: admin / owner
  • Контракт: CreateCustomFieldRequest → CreateCustomFieldResponse
  • Request: {projectId:string, name:string, key:string, type:CustomFieldType, required:boolean, options:string[], description:string, showOnCard:boolean, objectTypeIds:string[]}
  • Response: {field:CustomFieldDefinition}

key уникален в проекте и после создания не меняется. options нужны для типа SELECT. objectTypeIds явно задаёт типы, в контекст которых сразу добавить поле; пустой массив оставляет поле без привязок.

UpdateCustomField

  • Доступ: admin / owner
  • Контракт: UpdateCustomFieldRequest → UpdateCustomFieldResponse
  • Request: {fieldId:string, name?:string, required?:boolean, options:string[], replaceOptions:boolean, position?:int32, description?:string, showOnCard?:boolean}
  • Response: {field:CustomFieldDefinition}

Тип и key не изменяются. Для полной замены списка используйте replaceOptions = true.

DeleteCustomField

  • Доступ: admin / owner
  • Контракт: DeleteCustomFieldRequest → DeleteCustomFieldResponse
  • Request: {fieldId:string}
  • Response: {}

Удаляет определение и его значения карточек через каскадный foreign key только после явного подтверждения пользователя. Любая ссылка из workflow, фильтра, временной политики, автоматизации, бизнес-действия, расчёта, пакета, маршрутизации или дашборда блокирует удаление и остаётся без изменений.

UpdateFieldPermission

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: UpdateFieldPermissionRequest → UpdateFieldPermissionResponse
  • Request: {projectId:string, fieldKind:string, fieldId:string, viewRoleIds:string[], editRoleIds:string[], restricted:boolean, selfView:boolean}
  • Response: {permission:FieldPermission}

Задаёт доступ проектных ролей к системному полю, пользовательскому полю карточки, полю персонала или расчёту. fieldKind принимает system, custom, personnel или calculation. Для system используются стабильные идентификаторы содержательных полей карточки, например description, priority, assignee, story_points, labels, due_date, comments и checklists; ключ, название, тип объекта и состояние остаются навигационной основой карточки и не ограничиваются. Роль из editRoleIds должна также входить в viewRoleIds. При restricted:false правило удаляется и поле снова доступно всем ролям. Права нескольких ролей участника объединяются: достаточно одной роли с просмотром или изменением. Участник без права просмотра не получает определение или значение поля и не может использовать его в фильтрах, DSL, автоматизациях и действиях; без права изменения сервер отклоняет прямую запись. Для поля персонала selfView:true разрешает участнику видеть собственное значение, но не чужие значения и не даёт права редактирования.

Доски и общие фильтры

CreateSavedBoard

  • Доступ: write
  • Контракт: CreateSavedBoardRequest → CreateSavedBoardResponse
  • Request: {projectId:string, name:string, description:string, filter:BoardFilter, parentId?:string, color:string, defaultAccess:string, accessRules:BoardAccessRule[], subscriptionsEnabled:boolean}
  • Response: {board:SavedBoard}

Доска — сохранённое представление над общим набором карточек. parentId создаёт под-доску. defaultAccess обязателен: сервер не выбирает режим доступа за пользователя. Для помещения в существующую иерархию нужен manage к родительскому представлению.

UpdateSavedBoard

  • Доступ: ACL edit для содержимого, ACL manage для структуры и доступа
  • Контракт: UpdateSavedBoardRequest → UpdateSavedBoardResponse
  • Request: {boardId:string, name?:string, description?:string, filter?:BoardFilter, isDefault?:boolean, parentId?:string, position?:int32, color?:string, defaultAccess?:string, accessRules:BoardAccessRule[], replaceAccessRules:boolean, subscriptionsEnabled?:boolean}
  • Response: {board:SavedBoard}

Пустой parentId переносит доску на верхний уровень. Сервер запрещает циклы. Массив правил заменяется только при replaceAccessRules = true; его порядок значим. Выключение подписок сохраняет их определения.

DeleteSavedBoard

  • Доступ: ACL manage
  • Контракт: DeleteSavedBoardRequest → DeleteSavedBoardResponse
  • Request: {boardId:string}
  • Response: {}

Удаляет только само представление. Карточки проекта и остальные определения остаются. Перед удалением нужно назначить другое основное представление, явно переместить дочерние виды и удалить filter cards, подписки и ссылки автоматизаций. В проекте должна остаться хотя бы одна доска.

CreateAccessGroup

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: CreateAccessGroupRequest → CreateAccessGroupResponse
  • Request: {projectId:string, name:string, description:string, color:string, memberIds:string[], roleIds:string[]}
  • Response: {group:AccessGroup}

Создаёт группу только из участников того же workspace и ролей того же проекта. Пустые состав и список ролей допустимы; сервер ничего не подставляет автоматически. Итоговые права участника — объединение разрешений всех ролей всех его групп.

UpdateAccessGroup

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: UpdateAccessGroupRequest → UpdateAccessGroupResponse
  • Request: {groupId:string, name?:string, description?:string, color?:string, memberIds:string[], replaceMemberIds:boolean, roleIds:string[], replaceRoleIds:boolean, position?:int32}
  • Response: {group:AccessGroup}

Состав заменяется только при replaceMemberIds = true, назначения ролей — только при replaceRoleIds = true.

DeleteAccessGroup

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: DeleteAccessGroupRequest → DeleteAccessGroupResponse
  • Request: {groupId:string}
  • Response: {}

Удаление блокируется, пока хотя бы одно представление или шаблон формы ссылается на группу. Правила доступа не переписываются скрыто.

CreateProjectRole

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: CreateProjectRoleRequest → CreateProjectRoleResponse
  • Request: {projectId:string, name:string, description:string, color:string, permissions:string[]}
  • Response: {role:ProjectRole}

Роль создаётся без скрытых разрешений. Каждый ключ проверяется по серверному каталогу PermissionDefinition.

UpdateProjectRole

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: UpdateProjectRoleRequest → UpdateProjectRoleResponse
  • Request: {roleId:string, name?:string, description?:string, color?:string, permissions:string[], replacePermissions:boolean, position?:int32}
  • Response: {role:ProjectRole}

Разрешения атомарно заменяются только при replacePermissions = true.

DeleteProjectRole

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: DeleteProjectRoleRequest → DeleteProjectRoleResponse
  • Request: {roleId:string}
  • Response: {}

Удаление блокируется, пока роль прямо назначена хотя бы одному участнику либо назначена группе.

SetMemberProjectRoles

  • Доступ: access.manage; для agent token дополнительно scope admin
  • Контракт: SetMemberProjectRolesRequest → SetMemberProjectRolesResponse
  • Request: {projectId:string, memberId:string, roleIds:string[]}
  • Response: {assignments:MemberProjectRole[]}

Атомарно заменяет прямые назначения ролей участника в проекте. Все roleIds должны принадлежать этому проекту, а участник — его рабочему пространству. Пустой массив снимает прямые роли, но не меняет роли, полученные через группы доступа.

UpsertBoardSubscription

  • Доступ: ACL view; чужую подписку можно администрировать с views.manage или team.manage
  • Контракт: UpsertBoardSubscriptionRequest → UpsertBoardSubscriptionResponse
  • Request: {boardId:string, memberId:string, enabled:boolean, eventKinds:string[], includeOwn:boolean}
  • Response: {subscription:BoardSubscription}

Пустой memberId означает текущего участника. Включённая подписка требует хотя бы один явно выбранный ключ eventKinds; ключ соответствует ^[a-z][a-z0-9_.:-]{0,63}$. Новые определения можно сохранять только пока подписки разрешены представлением.

DeleteBoardSubscription

  • Доступ: ACL view; собственная подписка или owner
  • Контракт: DeleteBoardSubscriptionRequest → DeleteBoardSubscriptionResponse
  • Request: {subscriptionId:string}
  • Response: {}

MarkBoardSubscriptionRead

  • Доступ: ACL view; собственная подписка или owner
  • Контракт: MarkBoardSubscriptionReadRequest → MarkBoardSubscriptionReadResponse
  • Request: {subscriptionId:string}
  • Response: {subscription:BoardSubscription}

Сервер сохраняет lastReadAt. Виртуальные BoardNotification строятся из журнала событий и текущего соответствия карточки фильтру представления; отдельные копии уведомлений в БД не создаются.

MarkNotificationsRead

  • Доступ: project.view; только уведомления текущего участника
  • Контракт: MarkNotificationsReadRequest → MarkNotificationsReadResponse
  • Request: {projectId:string, notificationIds:string[], markAll:boolean}
  • Response: {}

При markAll = true сервер отмечает прочитанными все персональные уведомления текущего участника в проекте. Иначе требуется хотя бы один notificationId. Уведомления о @упоминаниях сохраняются отдельно от подписок на представления и дедуплицируются по комментарию и получателю.

CreateBoardFilterCard

  • Доступ: ACL view для личного, ACL edit для общего
  • Контракт: CreateBoardFilterCardRequest → CreateBoardFilterCardResponse
  • Request: {boardId:string, name:string, filter:BoardFilter, color:string, personal:boolean}
  • Response: {card:BoardFilterCard}

UpdateBoardFilterCard

  • Доступ: владелец личного фильтра с ACL view либо ACL edit для общего
  • Контракт: UpdateBoardFilterCardRequest → UpdateBoardFilterCardResponse
  • Request: {cardId:string, name?:string, filter?:BoardFilter, position?:int32, color?:string}
  • Response: {card:BoardFilterCard}

DeleteBoardFilterCard

  • Доступ: владелец личного фильтра с ACL view либо ACL edit для общего
  • Контракт: DeleteBoardFilterCardRequest → DeleteBoardFilterCardResponse
  • Request: {cardId:string}
  • Response: {}

Wiki

CreateWikiArticle

  • Доступ: write
  • Контракт: CreateWikiArticleRequest → CreateWikiArticleResponse
  • Request: {projectId:string, parentId?:string, title:string, body:string}
  • Response: {article:WikiArticle}

parentId можно не передавать для корневой статьи. body хранит HTML визуального редактора; старый Markdown остаётся читаемым и нормализуется интерфейсом при следующем сохранении.

UpdateWikiArticle

  • Доступ: write
  • Контракт: UpdateWikiArticleRequest → UpdateWikiArticleResponse
  • Request: {articleId:string, parentId?:string, title?:string, body?:string, position?:int32}
  • Response: {article:WikiArticle}

Пустой parentId переносит статью в корень. Иерархия не ограничена по глубине; циклы запрещены.

DeleteWikiArticle

  • Доступ: write
  • Контракт: DeleteWikiArticleRequest → DeleteWikiArticleResponse
  • Request: {articleId:string}
  • Response: {deletedArticleCount:int32, clearedFieldValueCount:int32}

Атомарно удаляет поддерево и очищает значения custom field типа WIKI_ARTICLE.

Автоматизации

CreateAutomationRule

  • Доступ: admin / owner
  • Контракт: CreateAutomationRuleRequest → CreateAutomationRuleResponse
  • Request: {projectId:string, name:string, enabled:boolean, trigger:AutomationTrigger, fromColumnId?:string, toColumnId?:string, actions:AutomationAction[], integrationId?:string, eventFilter:AutomationEventFilter}
  • Response: {rule:AutomationRule}

Для ISSUE_MOVED пустая граница колонки означает «любая». Для ISSUE_UPDATED фильтр eventFilter.changedFields проверяется по снимкам карточки до и после изменения; так назначение исполнителя не срабатывает на простое редактирование названия. Действия выполняются по порядку.

UpdateAutomationRule

  • Доступ: admin / owner
  • Контракт: UpdateAutomationRuleRequest → UpdateAutomationRuleResponse
  • Request: {ruleId:string, name?:string, enabled?:boolean, trigger?:AutomationTrigger, fromColumnId?:string, toColumnId?:string, actions:AutomationAction[], replaceActions:boolean, integrationId?:string, eventFilter:AutomationEventFilter, replaceEventFilter:boolean}
  • Response: {rule:AutomationRule}

Массив действий заменяется только с replaceActions = true.

DeleteAutomationRule

  • Доступ: admin / owner
  • Контракт: DeleteAutomationRuleRequest → DeleteAutomationRuleResponse
  • Request: {ruleId:string}
  • Response: {}

Удаление правила не удаляет его журнал запусков.

TestAutomationRule

  • Доступ: admin / owner
  • Контракт: TestAutomationRuleRequest → TestAutomationRuleResponse
  • Request: {ruleId:string, issueId:string, fromColumnId?:string, toColumnId?:string}
  • Response: {run:AutomationRun, changes:AutomationPreviewChange[]}

Выполняет dry-run на снимке выбранной карточки. Ответ показывает before и after, но не изменяет карточку, комментарии или связи. Тест записывается в projectState.automationRuns со статусом preview; ошибки в устаревших ссылках записываются со статусом failed.

Шаблоны создания

CreateIssueTemplate

  • Доступ: templates.manage; для agent token дополнительно scope admin
  • Контракт: CreateIssueTemplateRequest → CreateIssueTemplateResponse
  • Request: {projectId:string, name:string, description:string, enabled:boolean, access:IssueTemplateAccess, objectTypeId?:string, columnId?:string, fields:IssueTemplateField[], successMessage:string, expiresAt?:string, submissionLimit:int32, password:string, accessGroupIds:string[]}
  • Response: {template:IssueTemplate}

Создаёт форму без неявных полей. expiresAt передаётся в RFC 3339, submissionLimit = 0 означает отсутствие лимита.

UpdateIssueTemplate

  • Доступ: templates.manage; для agent token дополнительно scope admin
  • Контракт: UpdateIssueTemplateRequest → UpdateIssueTemplateResponse
  • Request: {templateId:string, name?:string, description?:string, enabled?:boolean, access?:IssueTemplateAccess, objectTypeId?:string, columnId?:string, fields:IssueTemplateField[], replaceFields:boolean, successMessage?:string, expiresAt?:string, submissionLimit?:int32, position?:int32, password?:string, clearPassword:boolean, accessGroupIds:string[], replaceAccessGroupIds:boolean}
  • Response: {template:IssueTemplate}

replaceFields = true атомарно заменяет состав и порядок формы. Пустые optional ID снимают фиксацию типа или колонки. Непустой password меняет пароль; его исходное значение никогда не возвращается. При смене режима с PASSWORD хеш удаляется. Группы заменяются только при replaceAccessGroupIds = true.

DeleteIssueTemplate

  • Доступ: templates.manage; для agent token дополнительно scope admin
  • Контракт: DeleteIssueTemplateRequest → DeleteIssueTemplateResponse
  • Request: {templateId:string}
  • Response: {}

Ссылка перестаёт работать, но IssueTemplateSubmission остаётся в журнале проекта.

  • Доступ: templates.manage; для agent token дополнительно scope admin
  • Контракт: RotateIssueTemplateLinkRequest → RotateIssueTemplateLinkResponse
  • Request: {templateId:string}
  • Response: {template:IssueTemplate}

Создаёт новый token и немедленно отзывает старую ссылку.

ResolveIssueTemplate

  • Доступ: согласно IssueTemplateAccess
  • Контракт: ResolveIssueTemplateRequest → ResolveIssueTemplateResponse
  • Request: {token:string, password:string}
  • Response: {form:IssueTemplateForm}

Возвращает только разрешённые поля, подписи и актуальные варианты выбора; внутренние ID конфигурации вне формы не раскрываются. Для PASSWORD без пароля возвращается метаинформация формы с requiresPassword = true, accessGranted = false и пустым fields.

SubmitIssueTemplate

  • Доступ: согласно IssueTemplateAccess
  • Контракт: SubmitIssueTemplateRequest → SubmitIssueTemplateResponse
  • Request: {token:string, values:IssueTemplateValue[], idempotencyKey:string, password:string}
  • Response: {issue:Issue, issueKey:string, successMessage:string, submission:IssueTemplateSubmission}

Повтор с тем же idempotencyKey возвращает ранее созданную карточку. Клиент может передавать только поля в режимах editable/required; hidden/locked берутся из определения.

Рабочее время и сигналы

CreateBusinessCalendar

  • Доступ: admin / owner
  • Контракт: CreateBusinessCalendarRequest → CreateBusinessCalendarResponse
  • Request: {projectId:string, name:string, description:string, timezone:string, schedule:BusinessDaySchedule[], exceptions:BusinessCalendarException[]}
  • Response: {calendar:BusinessCalendar}

timezone — IANA-name. schedule не получает серверных значений по умолчанию: нужен хотя бы один явно включённый день с непересекающимся интервалом HH:MM.

UpdateBusinessCalendar

  • Доступ: admin / owner
  • Контракт: UpdateBusinessCalendarRequest → UpdateBusinessCalendarResponse
  • Request: {calendarId:string, name?:string, description?:string, timezone?:string, schedule:BusinessDaySchedule[], replaceSchedule:boolean, exceptions:BusinessCalendarException[], replaceExceptions:boolean, position?:int32}
  • Response: {calendar:BusinessCalendar}

Массивы заменяются только с соответствующим replaceSchedule или replaceExceptions. Отключённый день не хранит интервалы.

DeleteBusinessCalendar

  • Доступ: admin / owner
  • Контракт: DeleteBusinessCalendarRequest → DeleteBusinessCalendarResponse
  • Request: {calendarId:string}
  • Response: {}

Удаление отклоняется, пока календарь указан хотя бы в одной TimePolicy.

CreateTimePolicy

  • Доступ: admin / owner
  • Контракт: CreateTimePolicyRequest → CreateTimePolicyResponse
  • Request: {projectId:string, calendarId:string, name:string, description:string, enabled:boolean, startField:string, durationMinutes:int32, warningMinutes:int32, filter:BoardFilter, stopFilter:BoardFilter}
  • Response: {policy:TimePolicy}

startField принимает created_at, updated_at, start_date, due_date или custom:<FIELD_UUID> для поля типа DATE. filter определяет область применения, stopFilter — условие остановки. Длительность считается только по интервалам выбранного календаря.

UpdateTimePolicy

  • Доступ: admin / owner
  • Контракт: UpdateTimePolicyRequest → UpdateTimePolicyResponse
  • Request: {policyId:string, calendarId?:string, name?:string, description?:string, enabled?:boolean, startField?:string, durationMinutes?:int32, warningMinutes?:int32, filter:BoardFilter, replaceFilter:boolean, stopFilter:BoardFilter, replaceStopFilter:boolean, position?:int32}
  • Response: {policy:TimePolicy}

filter и stopFilter заменяются независимо. Выключение enabled сохраняет политику и её ссылки.

DeleteTimePolicy

  • Доступ: admin / owner
  • Контракт: DeleteTimePolicyRequest → DeleteTimePolicyResponse
  • Request: {policyId:string}
  • Response: {}

Удаление отклоняется, пока policyId использует хотя бы одно SignalRule.

CreateSignalRule

  • Доступ: admin / owner
  • Контракт: CreateSignalRuleRequest → CreateSignalRuleResponse
  • Request: {projectId:string, name:string, description:string, enabled:boolean, color:string, filter:BoardFilter, timePolicyId:string, policyStatuses:string[]}
  • Response: {rule:SignalRule}

Правило должно содержать условия filter либо ссылку на политику с хотя бы одним policyStatuses: on_track, warning, breached, stopped. color выбирается из редактируемой палитры workspace.

UpdateSignalRule

  • Доступ: admin / owner
  • Контракт: UpdateSignalRuleRequest → UpdateSignalRuleResponse
  • Request: {ruleId:string, name?:string, description?:string, enabled?:boolean, color?:string, filter:BoardFilter, replaceFilter:boolean, timePolicyId?:string, policyStatuses:string[], replacePolicyStatuses:boolean, position?:int32}
  • Response: {rule:SignalRule}

Пустой timePolicyId убирает связь с политикой. Условия и статусы заменяются только при соответствующих replace... = true.

DeleteSignalRule

  • Доступ: admin / owner
  • Контракт: DeleteSignalRuleRequest → DeleteSignalRuleResponse
  • Request: {ruleId:string}
  • Response: {}

Удаляет конфигурацию правила, не меняя карточки и политики времени.

UpdateSignalState

  • Доступ: write
  • Контракт: UpdateSignalStateRequest → UpdateSignalStateResponse
  • Request: {ruleId:string, issueId:string, acknowledged:boolean}
  • Response: {signal:Signal}

acknowledged = true подтверждает текущее совпадение; false возвращает его в работу. Состояние хранится независимо от правила и карточки, а действие записывается в аудит.

Бизнес-действия

CreateBusinessAction

  • Доступ: admin / owner
  • Контракт: CreateBusinessActionRequest → CreateBusinessActionResponse
  • Request: {projectId:string, name:string, description:string, enabled:boolean, color:string, icon:string, confirmationText:string, filter:BoardFilter, inputFields:BusinessActionInputField[], operations:BusinessActionOperation[]}
  • Response: {action:BusinessAction}

filter определяет карточки, на которых видна команда. inputFields и operations не получают серверных значений по умолчанию; определение должно содержать хотя бы одно поле формы или изменение.

UpdateBusinessAction

  • Доступ: admin / owner
  • Контракт: UpdateBusinessActionRequest → UpdateBusinessActionResponse
  • Request: {actionId:string, name?:string, description?:string, enabled?:boolean, color?:string, icon?:string, confirmationText?:string, filter:BoardFilter, replaceFilter:boolean, inputFields:BusinessActionInputField[], replaceInputFields:boolean, operations:BusinessActionOperation[], replaceOperations:boolean, position?:int32}
  • Response: {action:BusinessAction}

Фильтр, поля формы и операции заменяются независимо только при соответствующем replace... = true. Ссылки операции на inputKey проверяются после полной замены формы.

DeleteBusinessAction

  • Доступ: admin / owner
  • Контракт: DeleteBusinessActionRequest → DeleteBusinessActionResponse
  • Request: {actionId:string}
  • Response: {}

Удаляется только определение. BusinessActionRun не имеет каскадной ссылки на него и остаётся доступным в журнале.

ExecuteBusinessAction

  • Доступ: write
  • Контракт: ExecuteBusinessActionRequest → ExecuteBusinessActionResponse
  • Request: {actionId:string, issueId:string, values:BusinessActionValue[]}
  • Response: {issue:Issue, run:BusinessActionRun}

Сервер повторно проверяет включённую возможность, применимость фильтра, обязательные ответы, типы значений, workflow и ссылки. Все операции, автоматизации, запись журнала и аудит выполняются в одной транзакции; при ошибке карточка не меняется, а отдельная запись запуска получает статус failed.

Вычисляемые поля

CreateCalculationField

  • Доступ: admin / owner
  • Контракт: CreateCalculationFieldRequest → CreateCalculationFieldResponse
  • Request: {projectId:string, name:string, key:string, description:string, enabled:boolean, color:string, objectTypeId?:string, kind:string, resultType:string, operator:string, operands:CalculationOperand[], formula:string, formulaReferences:CalculationFormulaReference[], relationTypeId:string, relationDirection:string, aggregateField:string, aggregation:string, relatedFilter:BoardFilter, decimalPlaces:int32, emptyPolicy:string, prefix:string, suffix:string, separator:string}
  • Response: {field:CalculationField}

kind выбирается явно: новая formula использует выражение и стабильные ссылки, rollup — связь, направление, агрегацию и relatedFilter. Старые определения с operator и operands продолжают вычисляться и могут быть сохранены через новый редактор. Сервер не подставляет бизнес-поля, константы или готовые формулы.

UpdateCalculationField

  • Доступ: admin / owner
  • Контракт: UpdateCalculationFieldRequest → UpdateCalculationFieldResponse
  • Request: {fieldId:string, name?:string, key?:string, description?:string, enabled?:boolean, color?:string, objectTypeId?:string, kind?:string, resultType?:string, operator?:string, operands:CalculationOperand[], replaceOperands:boolean, formula?:string, formulaReferences:CalculationFormulaReference[], replaceFormulaReferences:boolean, relationTypeId?:string, relationDirection?:string, aggregateField?:string, aggregation?:string, relatedFilter:BoardFilter, replaceRelatedFilter:boolean, decimalPlaces?:int32, emptyPolicy?:string, prefix?:string, suffix?:string, separator?:string, position?:int32}
  • Response: {field:CalculationField}

Операнды и фильтр связанных карточек заменяются только при соответствующих флагах replace.... Пустой objectTypeId снимает ограничение по типу объекта. Циклические ссылки между расчётами отклоняются.

DeleteCalculationField

  • Доступ: admin / owner
  • Контракт: DeleteCalculationFieldRequest → DeleteCalculationFieldResponse
  • Request: {fieldId:string}
  • Response: {}

Удаление отклоняется, если расчёт используется другой формулой, rollup, представлением, фильтром, политикой, сигналом или бизнес-действием. Аналогично нельзя удалить пользовательское поле, тип связи или область объекта, пока они входят в определение расчёта.

CreateCalculationConstant

  • Доступ: admin / owner
  • Контракт: CreateCalculationConstantRequest → CreateCalculationConstantResponse
  • Request: {projectId:string, name:string, key:string, description:string, type:string, value:string}
  • Response: {constant:CalculationConstant}

type равен number или text. Числовое значение должно быть конечным; текст может быть пустым. Константы не создаются автоматически.

UpdateCalculationConstant

  • Доступ: admin / owner
  • Контракт: UpdateCalculationConstantRequest → UpdateCalculationConstantResponse
  • Request: {constantId:string, name?:string, key?:string, description?:string, type?:string, value?:string, position?:int32}
  • Response: {constant:CalculationConstant}

Название и значение можно менять без обновления формул. Смена типа отклоняется, пока константа используется, чтобы не сделать зависимые выражения невалидными.

DeleteCalculationConstant

  • Доступ: admin / owner
  • Контракт: DeleteCalculationConstantRequest → DeleteCalculationConstantResponse
  • Request: {constantId:string}
  • Response: {}

Удаление отклоняется, если стабильная ссылка на константу остаётся хотя бы в одной формуле.

Управляемые пакеты

CreateWorkBatch

  • Доступ: admin / owner
  • Контракт: CreateWorkBatchRequest → CreateWorkBatchResponse
  • Request: {projectId:string, name:string, description:string, enabled:boolean, color:string, selectionMode:string, filter:BoardFilter, issueIds:string[], executionMode:string, operations:BatchOperation[], maxItems:int32}
  • Response: {batch:WorkBatch}

selectionMode обязан быть dynamic или snapshot, executionModeatomic или best_effort. Нужны явно выбранный цвет, лимит от 1 до 1000 и хотя бы одна операция.

UpdateWorkBatch

  • Доступ: admin / owner
  • Контракт: UpdateWorkBatchRequest → UpdateWorkBatchResponse
  • Request: {batchId:string, name?:string, description?:string, enabled?:boolean, color?:string, selectionMode?:string, filter:BoardFilter, replaceFilter:boolean, issueIds:string[], replaceIssueIds:boolean, executionMode?:string, operations:BatchOperation[], replaceOperations:boolean, maxItems?:int32, position?:int32}
  • Response: {batch:WorkBatch}

Фильтр, снимок ID и операции заменяются независимо только при соответствующем replace... = true. Изменение определения обновляет его версию и делает ранее полученный preview непригодным для запуска.

DeleteWorkBatch

  • Доступ: admin / owner
  • Контракт: DeleteWorkBatchRequest → DeleteWorkBatchResponse
  • Request: {batchId:string}
  • Response: {}

Удаляется только определение. Записи WorkBatchRun не имеют каскадной ссылки на пакет и остаются в журнале проекта.

PreviewWorkBatch

  • Доступ: write
  • Контракт: PreviewWorkBatchRequest → PreviewWorkBatchResponse
  • Request: {batchId:string}
  • Response: {preview:WorkBatchPreview}

Сервер заново получает динамический состав либо читает снимок, проверяет лимит, доступность карточек, workflow и каждую операцию без побочных эффектов. Ответ содержит статус и планируемые изменения каждой карточки, а также selectionHash.

ExecuteWorkBatch

  • Доступ: write
  • Контракт: ExecuteWorkBatchRequest → ExecuteWorkBatchResponse
  • Request: {batchId:string, selectionHash:string}
  • Response: {run:WorkBatchRun}

Перед выполнением сервер повторяет preview. Если изменились определение, состав или updatedAt любой карточки, запрос отклоняется. В режиме atomic ошибка откатывает весь набор; в режиме best_effort каждая карточка выполняется в собственной транзакции, а итог может быть partial.

Маршрутизация

CreateRoutingPolicy

  • Доступ: admin / owner
  • Контракт: CreateRoutingPolicyRequest → CreateRoutingPolicyResponse
  • Request: {projectId:string, name:string, description:string, enabled:boolean, color:string, triggers:AutomationTrigger[], manualEnabled:boolean, filter:BoardFilter, strategy:string, candidates:RoutingCandidate[], loadFilter:BoardFilter, fallbackMode:string, fallbackMemberId:string, terminal:boolean, failureMode:string}
  • Response: {policy:RoutingPolicy}

Нужен хотя бы один явно выбранный способ запуска и один кандидат. strategy выбирается из first_available, least_loaded, round_robin, weighted_cycle; сервер не подставляет стратегию, кандидатов, веса, лимиты, цвет, условия или резервное поведение.

UpdateRoutingPolicy

  • Доступ: admin / owner
  • Контракт: UpdateRoutingPolicyRequest → UpdateRoutingPolicyResponse
  • Request: {policyId:string, name?:string, description?:string, enabled?:boolean, color?:string, triggers:AutomationTrigger[], replaceTriggers:boolean, manualEnabled?:boolean, filter:BoardFilter, replaceFilter:boolean, strategy?:string, candidates:RoutingCandidate[], replaceCandidates:boolean, loadFilter:BoardFilter, replaceLoadFilter:boolean, fallbackMode?:string, fallbackMemberId?:string, terminal?:boolean, failureMode?:string, position?:int32}
  • Response: {policy:RoutingPolicy}

Триггеры, фильтр применимости, кандидаты и фильтр нагрузки заменяются независимо только при своих флагах replace.... Неактивный участник остаётся в определении и становится недоступным только при следующем расчёте.

DeleteRoutingPolicy

  • Доступ: admin / owner
  • Контракт: DeleteRoutingPolicyRequest → DeleteRoutingPolicyResponse
  • Request: {policyId:string}
  • Response: {}

Удаляется только определение. RoutingRun не имеет каскадных ссылок на правило, карточку или участника и остаётся в журнале.

PreviewRoutingPolicy

  • Доступ: write
  • Контракт: PreviewRoutingPolicyRequest → PreviewRoutingPolicyResponse
  • Request: {policyId:string, issueId:string}
  • Response: {preview:RoutingPreview}

Сервер проверяет применимость, активность и настроенную ёмкость каждого кандидата, считает нагрузку отдельным loadFilter, применяет выбранную стратегию и показывает резервное решение. Preview не меняет карточку и возвращает selectionHash.

ExecuteRoutingPolicy

  • Доступ: write
  • Контракт: ExecuteRoutingPolicyRequest → ExecuteRoutingPolicyResponse
  • Request: {policyId:string, issueId:string, selectionHash:string}
  • Response: {issue:Issue, run:RoutingRun}

Ручной запуск разрешён только при manualEnabled = true. Хеш становится недействительным после изменения правила, карточки, состояния нагрузки, доступности участников или успешной истории циклического распределения. Автоматические правила выполняются по position внутри транзакции создания, изменения или перемещения карточки; terminal останавливает дальнейшие правила, а failureMode = block откатывает внешнюю операцию, сохраняя отказ в журнале.

Операционные дашборды

CreateDashboard

  • Доступ: admin / owner
  • Контракт: CreateDashboardRequest → CreateDashboardResponse
  • Request: {projectId:string, name:string, description:string, enabled:boolean, color:string, scopeFilter:BoardFilter, widgets:DashboardWidget[], refreshMode:string, refreshSeconds:int32}
  • Response: {dashboard:Dashboard}

Нужен хотя бы один полностью настроенный виджет. Сервер не подставляет название, цвет, визуализацию, меру, агрегацию, период, размер или режим обновления. refreshMode выбирается между manual и interval; интервал задаётся явно в диапазоне 10–3600 секунд.

UpdateDashboard

  • Доступ: admin / owner
  • Контракт: UpdateDashboardRequest → UpdateDashboardResponse
  • Request: {dashboardId:string, name?:string, description?:string, enabled?:boolean, color?:string, scopeFilter:BoardFilter, replaceScopeFilter:boolean, widgets:DashboardWidget[], replaceWidgets:boolean, refreshMode?:string, refreshSeconds?:int32, position?:int32}
  • Response: {dashboard:Dashboard}

Общий фильтр и массив виджетов заменяются только при соответствующих флагах. Технические ID существующих виджетов сохраняются, поэтому клиент может сопоставить результаты следующего preview без зависимости от их названий или порядка.

DeleteDashboard

  • Доступ: admin / owner
  • Контракт: DeleteDashboardRequest → DeleteDashboardResponse
  • Request: {dashboardId:string}
  • Response: {}

Удаляется только определение. Карточки, их поля, связи, расчёты и история не меняются.

PreviewDashboard

  • Доступ: read
  • Контракт: PreviewDashboardRequest → PreviewDashboardResponse
  • Request: {dashboardId:string}
  • Response: {preview:DashboardPreview}

Сервер применяет общий фильтр, затем фильтр и временное окно каждого виджета. Результат содержит значение, точки или строки и исходные issueIds для прозрачного drill-down. Preview доступен наблюдателям, ничего не записывает и блокируется, если возможность либо сам дашборд выключены.

Интеграции

BeginYouGileConnection

  • Доступ: admin / owner
  • Контракт: BeginYouGileConnectionRequest → BeginYouGileConnectionResponse
  • Request: {projectId:string, login:string, password:string, baseUrl:string}
  • Response: {sessionId:string, companies:YouGileCompany[], expiresAt:Timestamp}

Проверяет логин и пароль через POST /api-v2/auth/companies. Пароль не пишется в базу и остаётся только в короткой серверной сессии подключения.

AuthorizeYouGileCompany

  • Доступ: admin / owner
  • Контракт: AuthorizeYouGileCompanyRequest → AuthorizeYouGileCompanyResponse
  • Request: {projectId:string, sessionId:string, companyId:string}
  • Response: {sessionId:string, projects:YouGileProject[], boards:YouGileBoard[], expiresAt:Timestamp, companyTaskCount?:int32, companyArchivedTaskCount?:int32}

Выпускает ключ для выбранной компании, сразу удаляет пароль из временной сессии и загружает доступные проекты, доски, колонки и задачи. Счётчики строятся по фактически полученным элементам всех страниц по 20 записей: paging.count YouGile описывает текущую страницу и не используется как общий итог. Активные и архивные задачи считаются отдельно. Ключ не возвращается клиенту.

FinishYouGileConnection

  • Доступ: admin / owner
  • Контракт: FinishYouGileConnectionRequest → FinishYouGileConnectionResponse
  • Request: {projectId:string, sessionId:string, boardId:string, name:string, targetBoardId?:string, targetColumnId?:string, limit:int32, sourceScope:string, boardIds:string[]}
  • Response: {integration:IntegrationConnection, preview:IntegrationRunResult, sourceBoard:YouGileBoard, sourceBoards:YouGileBoard[]}

Создаёт подключение с зашифрованным ключом и выполняет безопасный dry-run выбранной области YouGile. boardIds содержит одну или несколько досок; устаревающее поле boardId сохраняется для совместимости со старыми клиентами. sourceScope: board, project, company или auto. Для board сервер объединяет задачи всех выбранных досок без дублей. В auto сервер использует выбранные доски, если в них есть физические задачи, иначе их проекты, а для пустых проектов — всю компанию. Так сводки и зеркала импортируют оригинальные задачи с их реальными колонками, не имитируя недоступный через API виртуальный фильтр. После успешного preview временная сессия без пароля остаётся доступной 15 минут, чтобы пользователь мог вернуться к выбору и подключить другой набор досок без повторного входа. Реальный импорт запускается отдельным RunImport.

CreateIntegration

  • Доступ: admin / owner
  • Контракт: CreateIntegrationRequest → CreateIntegrationResponse
  • Request: {projectId:string, provider:IntegrationProvider, name:string, baseUrl:string, externalProjectId:string, query:string, username:string, token:string, webhookSecret:string, enabled:boolean}
  • Response: {integration:IntegrationConnection}

token и webhookSecret сохраняются зашифрованными и не возвращаются. Ответ сообщает только hasSecret и hasWebhookSecret.

UpdateIntegration

  • Доступ: admin / owner
  • Контракт: UpdateIntegrationRequest → UpdateIntegrationResponse
  • Request: {integrationId:string, name?:string, baseUrl?:string, externalProjectId?:string, query?:string, username?:string, token?:string, webhookSecret?:string, enabled?:boolean}
  • Response: {integration:IntegrationConnection}

Если secret-поле не передано или передано пустым, сервер сохраняет существующий секрет.

DeleteIntegration

  • Доступ: admin / owner
  • Контракт: DeleteIntegrationRequest → DeleteIntegrationResponse
  • Request: {integrationId:string}
  • Response: {}

TestIntegration

  • Доступ: admin / owner
  • Контракт: TestIntegrationRequest → TestIntegrationResponse
  • Request: {integrationId:string}
  • Response: {ok:boolean, message:string, integration:IntegrationConnection}

Проверяет сохранённые реквизиты и обновляет status/lastError подключения.

RunImport

  • Доступ: admin / owner
  • Контракт: RunImportRequest → RunImportResponse
  • Request: {integrationId:string, targetBoardId?:string, targetColumnId?:string, dryRun:boolean, limit:int32}
  • Response: {result:IntegrationRunResult, integration:IntegrationConnection}

Поддерживает Jira и YouGile. Всегда начинайте с dryRun = true. Допустимый limit для MCP — 1–1000; значение по умолчанию — 1000.

YouGile читает задачи и пользователей страницами максимум по 20 элементов. Реальный запуск переносит карточки, пользователей, первого доступного исполнителя, автора, чек-листы, иерархию подзадач, зависимости и редактируемые связи. IntegrationRunResult возвращает отдельные счётчики этих сущностей. Чаты ставятся в durable-очередь и читаются по 20 сообщений: автор, исходное время, текст и служебные метаданные сохраняются, повторный запуск обновляет сообщения по внешнему ID.

Пока очередь работает, integration.status = "syncing", а integration.syncProgress содержит человекочитаемый прогресс. После перезапуска сервера незавершённая очередь продолжается. Безопасные GET-запросы повторяются до пяти раз при сетевом обрыве, timeout, 429 и 5xx; фоновая очередь дополнительно повторяет неудачную страницу до десяти раз с нарастающей задержкой.

SyncIntegration

  • Доступ: admin / owner
  • Контракт: SyncIntegrationRequest → SyncIntegrationResponse
  • Request: {integrationId:string}
  • Response: {result:IntegrationRunResult, integration:IntegrationConnection}

Для GitLab ищет ключи карточек в merge requests и обновляет Issue.externalLinks.

RetryGitLabOutboxAction

  • Доступ: admin / owner
  • Контракт: RetryGitLabOutboxActionRequest → RetryGitLabOutboxActionResponse
  • Request: {actionId:string}
  • Response: {action:GitLabOutboxAction}

Возвращает неуспешное действие в очередь немедленного выполнения. Успешное действие повторно не запускается.

Агенты и токены

Управлять agent principals может только браузерный пользователь с api.manage. Сам агент не может создавать, менять или ставить на паузу других агентов даже при admin-token.

ListAgents

  • Доступ: api.manage, только пользователь
  • Контракт: ListAgentsRequest → ListAgentsResponse
  • Request: {workspaceId:string, projectId:string}
  • Response: {agents:Agent[]}

Возвращает agents рабочего пространства, их роли в указанном проекте, capability policies и токены без секретов.

CreateAgent

  • Доступ: api.manage, только пользователь
  • Контракт: CreateAgentRequest → CreateAgentResponse
  • Request: {workspaceId:string, projectId:string, name:string, description:string, avatarColor:string, runtime:string, model:string, projectRoleIds:string[], capabilities:AgentCapabilityPolicy[], tokenScopes:string[], tokenExpiresInDays:int32}
  • Response: {agent:Agent, token:AgentToken, secret:string}

Атомарно создаёт agent principal, его служебного участника, проектные роли и первый токен. В проекте без granular roles базовый Viewer/Editor выводится из capability policy.

UpdateAgent

  • Доступ: api.manage, только пользователь
  • Контракт: UpdateAgentRequest → UpdateAgentResponse
  • Request: {agentId:string, projectId:string, name?:string, description?:string, avatarColor?:string, status?:string, runtime?:string, model?:string, projectRoleIds:string[], replaceProjectRoles:boolean, capabilities:AgentCapabilityPolicy[], replaceCapabilities:boolean}
  • Response: {agent:Agent}

Меняет профиль, active/paused status, роли текущего проекта и capability policy. Каждое изменение увеличивает configVersion; пауза блокирует все токены агента со следующего запроса.

ListAgentTokens

  • Доступ: admin / owner
  • Контракт: ListAgentTokensRequest → ListAgentTokensResponse
  • Request: {workspaceId:string, projectId?:string}
  • Response: {tokens:AgentToken[]}

Секреты не возвращаются. projectId нужно передавать как контекст проверки api.manage.

CreateAgentToken

  • Доступ: admin / owner
  • Контракт: CreateAgentTokenRequest → CreateAgentTokenResponse
  • Request: {workspaceId:string, name:string, scopes:string[], expiresInDays:int32, projectId?:string, agentId?:string}
  • Response: {token:AgentToken, secret:string}

projectId нужно передавать как контекст проверки api.manage. При заданном agentId выпускается ещё один токен существующего principal. Без agentId старый клиент совместимости создаёт новый first-class Agent. scopes содержит read, write и/или admin; admin автоматически включает read и write. Полный secret формата ab_agent_… возвращается только один раз.

RevokeAgentToken

  • Доступ: admin / owner
  • Контракт: RevokeAgentTokenRequest → RevokeAgentTokenResponse
  • Request: {tokenId:string, projectId?:string}
  • Response: {}

projectId нужно передавать как контекст проверки api.manage. Отзыв действует со следующего API-вызова.

ListAgentApprovalReceipts

Путь: POST /algoboard.v1.AgentApprovalService/ListAgentApprovalReceipts.

  • Доступ: api.manage, только пользователь
  • Контракт: ListAgentApprovalReceiptsRequest → ListAgentApprovalReceiptsResponse
  • Request: {projectId:string, statuses:string[], limit:int32}
  • Response: {approvals:AgentApprovalReceipt[]}

Возвращает последние receipts проекта, включая pending-запросы и историю решений/исполнений. statuses может содержать pending, approved, rejected, executing, executed, failed, expired; пустой список означает все статусы. limit — 1–100, значение 0 использует серверный default 50. Перед чтением сервер помечает просроченные pending/approved receipts как expired.

ReviewAgentApprovalReceipt

Путь: POST /algoboard.v1.AgentApprovalService/ReviewAgentApprovalReceipt.

  • Доступ: api.manage, только пользователь
  • Контракт: ReviewAgentApprovalReceiptRequest → ReviewAgentApprovalReceiptResponse
  • Request: {approvalId:string, projectId:string, decision:string, comment:string}
  • Response: {approval:AgentApprovalReceipt}

decision принимает approved или rejected. Рассмотреть можно только ещё не истёкший pending receipt. Approve не выполняет сохранённый запрос: Agent должен повторить тот же вызов с X-Algoboard-Approval: <approvalId>. Перед handler сервер атомарно переводит receipt в executing, а после ответа — в executed или failed.

ListAgentRuns

Путь: POST /algoboard.v1.AgentRunService/ListAgentRuns.

  • Доступ: api.manage, только пользователь
  • Контракт: ListAgentRunsRequest → ListAgentRunsResponse
  • Request: {projectId:string, agentId:string, statuses:string[], limit:int32}
  • Response: {runs:AgentRun[]}

Возвращает до 200 последних запусков выбранного проекта и workspace-level вызовы его Agent control plane. agentId и statuses необязательны; limit=0 использует default 100. Статусы: running, succeeded, failed, denied, approval_required. Записи running, которые не завершились за две минуты, при чтении переводятся в failed как прерванные.

ListManagedSkills

Путь: POST /algoboard.v1.AgentSkillService/ListManagedSkills.

  • Доступ: api.manage, только пользователь
  • Контракт: ListManagedSkillsRequest → ListManagedSkillsResponse
  • Request: {workspaceId:string, projectId:string}
  • Response: {skills:ManagedSkill[]}

Возвращает workspace-каталог skills в Codex-совместимом формате с последней опубликованной версией. Тот же архив может выполнять app-scoped runner Codex или Claude Code; архив в списке не передаётся.

ListManagedSkillVersions

Путь: POST /algoboard.v1.AgentSkillService/ListManagedSkillVersions.

  • Доступ: api.manage, только пользователь
  • Контракт: ListManagedSkillVersionsRequest → ListManagedSkillVersionsResponse
  • Request: {projectId:string, skillId:string}
  • Response: {versions:ManagedSkillVersion[]}

Возвращает immutable-историю выбранного skill от самой новой версии к самой старой. Архивы не передаются; любую версию можно отдельно скачать или повторно закрепить за назначением без изменения latest.

CreateManagedSkill

Путь: POST /algoboard.v1.AgentSkillService/CreateManagedSkill.

  • Доступ: api.manage, только пользователь
  • Контракт: CreateManagedSkillRequest → CreateManagedSkillResponse
  • Request: {workspaceId:string, projectId:string, skillMd:string}
  • Response: {skill:ManagedSkill, version:ManagedSkillVersion}

Создаёт ZIP версии 1 из полного SKILL.md и автоматически добавляет agents/openai.yaml с allow_implicit_invocation: false.

UploadManagedSkillVersion

Путь: POST /algoboard.v1.AgentSkillService/UploadManagedSkillVersion.

  • Доступ: api.manage, только пользователь
  • Контракт: UploadManagedSkillVersionRequest → UploadManagedSkillVersionResponse
  • Request: {workspaceId:string, projectId:string, skillId?:string, filename:string, archive:bytes}
  • Response: {skill:ManagedSkill, version:ManagedSkillVersion}

Принимает ZIP до 2 МБ. Проверяются пути, ссылки, размеры распаковки, единственный корневой SKILL.md, YAML frontmatter и совпадение папки с name. Пустой skillId создаёт skill или новую версию существующего slug.

DownloadManagedSkillVersion

Путь: POST /algoboard.v1.AgentSkillService/DownloadManagedSkillVersion.

  • Доступ: пользователь с api.manage либо Agent, которому назначена эта версия
  • Контракт: DownloadManagedSkillVersionRequest → DownloadManagedSkillVersionResponse
  • Request: {projectId:string, versionId:string}
  • Response: {skill:ManagedSkill, version:ManagedSkillVersion, filename:string, archive:bytes}

Agent может скачать только pinned-версию собственного активного назначения в проекте.

ListAgentSkillAssignments

Путь: POST /algoboard.v1.AgentSkillService/ListAgentSkillAssignments.

  • Доступ: пользователь с api.manage либо текущий Agent
  • Контракт: ListAgentSkillAssignmentsRequest → ListAgentSkillAssignmentsResponse
  • Request: {projectId:string, agentId?:string}
  • Response: {assignments:AgentSkillAssignment[]}

Для Agent agentId принудительно ограничивается его собственной identity.

UpsertAgentSkillAssignment

Путь: POST /algoboard.v1.AgentSkillService/UpsertAgentSkillAssignment.

  • Доступ: api.manage, только пользователь
  • Контракт: UpsertAgentSkillAssignmentRequest → UpsertAgentSkillAssignmentResponse
  • Request: {projectId:string, agentId:string, skillVersionId:string, command:string, approvalPolicy:string, enabled:bool, configJson:string}
  • Response: {assignment:AgentSkillAssignment}

Закрепляет точную версию и slash-command за агентом. approvalPolicy принимает allow, ask, deny; configJson должен быть JSON-объектом.

DeleteAgentSkillAssignment

Путь: POST /algoboard.v1.AgentSkillService/DeleteAgentSkillAssignment.

  • Доступ: api.manage, только пользователь
  • Контракт: DeleteAgentSkillAssignmentRequest → DeleteAgentSkillAssignmentResponse
  • Request: {projectId:string, assignmentId:string}
  • Response: {}

Отключает назначение, отменяет ещё не запущенные invocations и скрывает slash-команду, но сохраняет skill, версии и полную историю.

InvokeAgentSkill

Путь: POST /algoboard.v1.AgentSkillService/InvokeAgentSkill.

  • Доступ: chat.send, только пользователь
  • Контракт: InvokeAgentSkillRequest → InvokeAgentSkillResponse
  • Request: {projectId:string, conversationId:string, clientRequestId:string, command:string, arguments:string, threadRootId?:string}
  • Response: {invocation:AgentSkillInvocation, message:ChatMessage, duplicate:bool, runnerOnline:bool}

Создаёт обычное сообщение /<command> <arguments> и app-only invocation. Команда разрешается только среди активных Agent principal текущего разговора; повторный clientRequestId возвращает тот же запуск. runnerOnline=false позволяет интерфейсу сразу предупредить, что invocation останется в очереди до подключения локального runner.

ListAgentSkillInvocations

Путь: POST /algoboard.v1.AgentSkillService/ListAgentSkillInvocations.

  • Доступ: api.manage, только пользователь
  • Контракт: ListAgentSkillInvocationsRequest → ListAgentSkillInvocationsResponse
  • Request: {projectId:string, conversationId?:string, agentId?:string, statuses:string[], limit:int32}
  • Response: {invocations:AgentSkillInvocation[]}

Статусы: pending_approval, queued, running, succeeded, failed, rejected, cancelled.

ReviewAgentSkillInvocation

Путь: POST /algoboard.v1.AgentSkillService/ReviewAgentSkillInvocation.

  • Доступ: api.manage, только пользователь
  • Контракт: ReviewAgentSkillInvocationRequest → ReviewAgentSkillInvocationResponse
  • Request: {projectId:string, invocationId:string, decision:string, comment:string}
  • Response: {invocation:AgentSkillInvocation}

decision принимает approved или rejected. Одобрение атомарно переводит pending invocation в очередь runner.

ListRunnableAgentSkillInvocations

Путь: POST /algoboard.v1.AgentSkillService/ListRunnableAgentSkillInvocations.

  • Доступ: только Agent token со scope read
  • Контракт: ListRunnableAgentSkillInvocationsRequest → ListRunnableAgentSkillInvocationsResponse
  • Request: {projectId:string, limit:int32}
  • Response: {invocations:AgentSkillInvocation[]}

Возвращает только queued invocations текущего Agent principal. Перед чтением сервер восстанавливает его running invocations с просроченным lease: активное назначение возвращается в очередь, отключённое отменяется, а запуск с тремя исчерпанными попытками завершается статусом failed.

ClaimAgentSkillInvocation

Путь: POST /algoboard.v1.AgentSkillService/ClaimAgentSkillInvocation.

  • Доступ: только Agent token со scope write
  • Контракт: ClaimAgentSkillInvocationRequest → ClaimAgentSkillInvocationResponse
  • Request: {projectId:string, invocationId:string, runnerId:string}
  • Response: {invocation:AgentSkillInvocation, delegationToken:string}

Атомарно переводит запуск из queued в running, увеличивает attemptCount и выдаёт двухминутный lease в heartbeatAt / leaseExpiresAt; повторный claim другого runner отклоняется. delegationToken предназначен только для встроенного managed MCP и наследует текущие права requestedByMemberId; постоянный connector token в дочерний runtime не передаётся.

HeartbeatAgentSkillInvocation

Путь: POST /algoboard.v1.AgentSkillService/HeartbeatAgentSkillInvocation.

  • Доступ: только Agent token со scope write
  • Контракт: HeartbeatAgentSkillInvocationRequest → HeartbeatAgentSkillInvocationResponse
  • Request: {projectId:string, invocationId:string, runnerId:string}
  • Response: {invocation:AgentSkillInvocation}

Продлевает lease ещё на две минуты только для текущего runnerId. После возврата запуска в очередь старый runner получает conflict и больше не может heartbeat или finish эту попытку.

FinishAgentSkillInvocation

Путь: POST /algoboard.v1.AgentSkillService/FinishAgentSkillInvocation.

  • Доступ: только Agent token со scope write
  • Контракт: FinishAgentSkillInvocationRequest → FinishAgentSkillInvocationResponse
  • Request: {projectId:string, invocationId:string, runnerId:string, succeeded:bool, output:string, errorMessage:string}
  • Response: {invocation:AgentSkillInvocation}

Завершить running invocation может только runner, который его занял. Terminal transition освобождает lease. Сервер ограничивает сохраняемый output последними 100 000 символами.

ListAgentConnectors

Путь: POST /algoboard.v1.AgentSkillService/ListAgentConnectors.

  • Доступ: api.manage, только пользователь
  • Контракт: ListAgentConnectorsRequest → ListAgentConnectorsResponse
  • Request: {projectId:string, agentId?:string}
  • Response: {connectors:AgentConnector[]}

Возвращает локальные подключения проекта. status, mcpOnline и runnerOnline вычисляются по отдельным heartbeat; presence считается online 90 секунд после последнего сигнала.

CreateAgentConnectorPairing

Путь: POST /algoboard.v1.AgentSkillService/CreateAgentConnectorPairing.

  • Доступ: api.manage, только пользователь
  • Контракт: CreateAgentConnectorPairingRequest → CreateAgentConnectorPairingResponse
  • Request: {workspaceId:string, projectId:string, agentId:string, scopes:string[]}
  • Response: {pairing:AgentConnectorPairing, code:string, connectCommand:string}

Создаёт высокоэнтропийный одноразовый код на 10 минут. Сервер хранит только SHA-256 hash. Запрошенные scopes не могут обходить policy выбранного Agent.

ExchangeAgentConnectorPairing

Путь: POST /algoboard.v1.AgentSkillService/ExchangeAgentConnectorPairing.

  • Доступ: публичный RPC с одноразовым pairing code
  • Контракт: ExchangeAgentConnectorPairingRequest → ExchangeAgentConnectorPairingResponse
  • Request: {code:string, name:string, runtime:string, version:string, hostname:string, platform:string, projectDir:string}
  • Response: {connector:AgentConnector, token:AgentToken, secret:string, apiUrl:string}

Атомарно помечает pairing использованным, создаёт отдельный отзываемый Agent token и связывает его с connector. Повторный, просроченный или неизвестный code возвращает одинаковую unauthenticated-ошибку. secret возвращается один раз.

GetAgentConnector

Путь: POST /algoboard.v1.AgentSkillService/GetAgentConnector.

  • Доступ: токен именно этого connector либо пользователь с api.manage
  • Контракт: GetAgentConnectorRequest → GetAgentConnectorResponse
  • Request: {projectId:string, connectorId:string}
  • Response: {connector:AgentConnector}

Даже другой действующий token того же Agent не может присвоить connector и читать его локальную metadata.

HeartbeatAgentConnector

Путь: POST /algoboard.v1.AgentSkillService/HeartbeatAgentConnector.

  • Доступ: токен именно этого connector, scope read
  • Контракт: HeartbeatAgentConnectorRequest → HeartbeatAgentConnectorResponse
  • Request: {projectId:string, connectorId:string, component:string, runtime:string, version:string, hostname:string, platform:string, projectDir:string}
  • Response: {connector:AgentConnector}

component принимает mcp или runner. Раздельные timestamps не позволяют одному локальному процессу затереть presence другого; heartbeat не заполняет execution ledger.

DisconnectAgentConnector

Путь: POST /algoboard.v1.AgentSkillService/DisconnectAgentConnector.

  • Доступ: токен именно этого connector либо пользователь с api.manage
  • Контракт: DisconnectAgentConnectorRequest → DisconnectAgentConnectorResponse
  • Request: {projectId:string, connectorId:string}
  • Response: {connector:AgentConnector}

Идемпотентно переводит connector в disconnected и в той же транзакции отзывает связанный Agent token. История connector остаётся видимой владельцу проекта.

ManagedSkill

{id, workspaceId, slug, name, description, status, latestVersion,
 createdByMemberId, createdAt, updatedAt}

ManagedSkillVersion

{id, skillId, version, archiveSha256, archiveSize, skillMd,
 validationWarnings, createdByMemberId, createdAt}

AgentSkillAssignment

{id, workspaceId, projectId, agentId, skillId, skillVersionId,
 command, approvalPolicy, enabled, configJson, skill, skillVersion, agent,
 createdAt, updatedAt}

AgentSkillInvocation

{id, workspaceId, projectId, conversationId, messageId, assignmentId,
 agentId, skillId, skillVersionId, command, arguments, status,
 requestedByMemberId, reviewedByMemberId, reviewComment, runnerId,
 attemptCount, output, errorMessage, assignment, requestedAt, reviewedAt,
 startedAt, heartbeatAt, leaseExpiresAt, finishedAt, updatedAt}

AgentConnector

{id, workspaceId, projectId, agentId, tokenId, name, runtime, version,
 hostname, platform, projectDir, status, mcpOnline, runnerOnline,
 lastSeenAt, lastMcpHeartbeatAt, lastRunnerHeartbeatAt, disconnectedAt,
 createdAt, updatedAt}

AgentConnectorPairing

{id, workspaceId, projectId, agentId, scopes, expiresAt, usedAt, createdAt}

Модели данных

Правила protobuf JSON

  • Proto snake_case превращается в JSON lowerCamelCase.
  • google.protobuf.Timestamp — RFC 3339 UTC, например "2026-07-25T14:30:00Z".
  • bytes — Base64-строка.
  • int64 в protobuf JSON передаётся строкой, чтобы JavaScript не терял точность.
  • Enum рекомендуется передавать символическим именем. Числовые значения поддерживаются для совместимости.
  • Неустановленное optional-поле отсутствует в JSON. Обычное поле с default-значением также может отсутствовать в ответе.
  • Календарные поля startDate, endDate и dueDate — строки YYYY-MM-DD, а не Timestamp.
  • UUID всегда передаётся строкой. Используйте ID из свежего GetAppState, не угадывайте их.

Основные сущности

Workspace

{
  id:string,
  name:string,
  timezone:string,
  defaultSprintDays:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

ColorStyle

{
  id:string,
  workspaceId:string,
  name:string,
  key:string,
  value:string,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

value хранит редактируемый HEX, а стабильный key используется в цветовых полях остальных сущностей.

Member

{
  id:string,
  workspaceId:string,
  name:string,
  email:string,
  role:MemberRole,
  avatarColor:string,
  active:boolean,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  avatarData:bytes,
  avatarContentType:string,
  accountLogin:string,
  principalType:string
}

avatarData приходит Base64-строкой. Пароль и его hash никогда не входят в API-ответ.

PersonnelFieldDefinition

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  type:string,
  options:string[],
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

PersonnelFieldValue

{fieldId:string, value:string}

MemberProjectProfile

{
  projectId:string,
  memberId:string,
  businessCalendarId?:string,
  values:PersonnelFieldValue[],
  createdAt:Timestamp,
  updatedAt:Timestamp
}

AuthSession

{
  authenticated:boolean,
  member?:Member,
  workspace?:Workspace,
  expiresAt?:Timestamp
}

Project

{
  id:string,
  workspaceId:string,
  name:string,
  key:string,
  color:string,
  archived:boolean,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

ProjectCapability

{
  id:string,
  projectId:string,
  key:string,
  name:string,
  description:string,
  icon:string,
  color:string,
  enabled:boolean,
  position:int32,
  configured:boolean,
  createdAt?:Timestamp,
  updatedAt?:Timestamp
}

configured:false означает, что проект ещё не сохранил собственную конфигурацию этой возможности. Для нового проекта все опциональные возможности возвращаются выключенными. Начальные названия служат редактируемым описанием доступных движков, а не создают объекты, поля или правила.

BoardColumn

{
  id:string,
  projectId:string,
  name:string,
  position:int32,
  wipLimit:int32,
  category:ColumnCategory,
  objectTypeId?:string
}

ObjectType

{
  id:string,
  projectId:string,
  name:string,
  pluralName:string,
  key:string,
  description:string,
  icon:string,
  color:string,
  titleLabel:string,
  position:int32,
  archived:boolean,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

ObjectTypeField

{
  objectTypeId:string,
  fieldId:string,
  label:string,
  description:string,
  required:boolean,
  showOnCard:boolean,
  visibleOnCreate:boolean,
  readOnly:boolean,
  position:int32,
  defaultValue:string
}

RelationType

{
  id:string,
  projectId:string,
  sourceObjectTypeId:string,
  targetObjectTypeId:string,
  name:string,
  reverseName:string,
  key:string,
  cardinality:RelationCardinality,
  color:string,
  position:int32,
  archived:boolean,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

ObjectRelation

{
  id:string,
  projectId:string,
  relationTypeId:string,
  sourceIssueId:string,
  targetIssueId:string,
  createdAt:Timestamp
}

Sprint

{
  id:string,
  projectId:string,
  name:string,
  goal:string,
  status:SprintStatus,
  startDate:string,
  endDate:string,
  issueCount:int32,
  storyPoints:int32,
  completedPoints:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  completedAt?:Timestamp
}

Label

{id:string, projectId:string, name:string, color:string}

Карточка и журнал

Issue

{
  id:string,
  projectId:string,
  columnId:string,
  sprintId?:string,
  assigneeId?:string,
  reporterId?:string,
  number:int32,
  key:string,
  title:string,
  description:string,
  type:IssueType,
  priority:IssuePriority,
  storyPoints:int32,
  position:int32,
  labels:Label[],
  comments:Comment[],
  archived:boolean,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  completedAt?:Timestamp,
  startDate?:string,
  dueDate?:string,
  baselineStartDate?:string,
  baselineDueDate?:string,
  progressPercent:int32,
  actualStartDate?:string,
  actualFinishDate?:string,
  milestone:boolean,
  deadlineDate?:string,
  parentId?:string,
  dependencyIds:string[],
  timelineDependencies:TimelineDependency[],
  customFieldValues:CustomFieldValue[],
  blockedReason:string,
  externalLinks:ExternalLink[],
  objectTypeId?:string,
  checklists:IssueChecklist[],
  timelinePosition:int32,
  commentCount:int32
}

dependencyIds — карточки, завершения которых текущая карточка ждёт. parentId задаёт родительскую карточку; сервер запрещает циклы и связи между проектами.

TimelineDependency

{
  predecessorIssueId:string,
  type:TimelineDependencyType,
  lagDays:int32
}

TimelineDependencyInput

{
  predecessorIssueId:string,
  type:TimelineDependencyType,
  lagDays:int32
}

lagDays ограничен диапазоном от −365 до 365 и считается по рабочему календарю исполнителя последующей карточки.

CustomFieldInput

{fieldId:string, value:string}

CustomFieldValue

{fieldId:string, issueId:string, value:string}

IssueChecklist

{
  id:string,
  issueId:string,
  title:string,
  position:int32,
  items:IssueChecklistItem[]
}

IssueChecklistItem

{id:string, checklistId:string, title:string, completed:boolean, position:int32}

IssueChecklistInput

{id?:string, title:string, items:IssueChecklistItemInput[]}

IssueChecklistItemInput

{id?:string, title:string, completed:boolean}

При replaceChecklists = true сервер полностью синхронизирует чек-листы карточки; пустой массив удаляет их.

Comment

{
  id:string,
  issueId:string,
  authorId:string,
  authorName:string,
  body:string,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  source:string,
  metadataJson:string
}

ActivityEvent

{
  id:string,
  projectId:string,
  issueId?:string,
  actorId?:string,
  actorName:string,
  kind:string,
  summary:string,
  createdAt:Timestamp
}
{
  id:string,
  issueId:string,
  provider:IntegrationProvider,
  kind:string,
  externalId:string,
  title:string,
  url:string,
  status:string,
  updatedAt:Timestamp
}

Доски и процесс

BoardFilterRule

{field:string, operator:BoardFilterOperator, values:string[]}

BoardSwimlaneCondition

{
  rules:BoardFilterRule[],
  queryDsl:string,
  queryReferences:CalculationFormulaReference[]
}

Быстрые rules и условие queryDsl одной дорожки объединяются по and. queryReferences сохраняют стабильные привязки полей, использованных в DSL.

BoardSwimlaneRule

{
  id:string,
  name:string,
  color:string,
  condition:BoardSwimlaneCondition
}

Правила проверяются сверху вниз: карточка попадает в первую подходящую дорожку.

BoardSwimlaneConfig

{
  mode:BoardSwimlaneMode,
  field:string,
  rules:BoardSwimlaneRule[],
  showUnmatched:boolean,
  showEmpty:boolean
}

В режиме FIELD сервер группирует карточки по field. В режиме RULES использует упорядоченные rules. showUnmatched добавляет дорожку для карточек, не прошедших ни одно правило, а showEmpty сохраняет в ответе пустые дорожки.

BoardFilter

{
  query:string,
  assigneeIds:string[],
  labelIds:string[],
  priorities:IssuePriority[],
  sprintId?:string,
  onlyUnassigned:boolean,
  onlyOverdue:boolean,
  includeCompleted:boolean,
  rules:BoardFilterRule[],
  visibleColumnIds:string[],
  cardFieldKeys:string[],
  cardFieldsConfigured:boolean,
  queryDsl:string,
  queryReferences:CalculationFormulaReference[],
  swimlanes:BoardSwimlaneConfig
}

rules соединяются по AND. Несколько values внутри одного правила соединяются по OR. queryDsl хранит только логическое условие, например [Приоритет] >= 3 and [Срок] < @today; служебные select Карточка, from Карточки и limit 500 добавляет сервер. queryReferences фиксируют привязку читаемых имён к стабильным источникам. Если заполнены и queryDsl, и быстрые параметры фильтра, они применяются вместе по правилу and.

SavedBoard

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  filter:BoardFilter,
  isDefault:boolean,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  parentId?:string,
  position:int32,
  color:string,
  defaultAccess:string,
  accessRules:BoardAccessRule[],
  subscriptionsEnabled:boolean,
  currentAccess:string
}

BoardAccessRule

{
  subjectType:string,
  subjectId:string,
  access:string
}

subjectTypemember или group; accessnone, view, edit или manage. Порядок элементов определяет приоритет.

AccessGroup

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  color:string,
  memberIds:string[],
  roleIds:string[],
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

PermissionDefinition

{key:string, category:string, name:string, description:string}

ProjectRole

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  color:string,
  permissions:string[],
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

MemberProjectRole

{
  projectId:string,
  memberId:string,
  roleId:string,
  createdAt:Timestamp
}

BoardSubscription

{
  id:string,
  projectId:string,
  boardId:string,
  memberId:string,
  enabled:boolean,
  eventKinds:string[],
  includeOwn:boolean,
  lastReadAt?:Timestamp,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

BoardNotification

{
  subscriptionId:string,
  boardId:string,
  activityId:string,
  issueId:string,
  actorId:string,
  kind:string,
  summary:string,
  read:boolean,
  occurredAt:Timestamp
}

MemberNotification

{
  id:string,
  projectId:string,
  memberId:string,
  actorId:string,
  issueId:string,
  commentId:string,
  kind:string,
  summary:string,
  read:boolean,
  createdAt:Timestamp
}

BoardFilterCard

{
  id:string,
  boardId:string,
  name:string,
  filter:BoardFilter,
  position:int32,
  color:string,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  personal:boolean
}

personal не раскрывает идентификатор владельца. Если значение истинно, объект уже отфильтрован сервером для текущего участника и недоступен другим пользователям даже по ID.

WorkflowRule

{
  id:string,
  projectId:string,
  fromColumnId:string,
  toColumnId:string,
  enabled:boolean,
  allowedRoles:MemberRole[],
  allowedProjectRoleIds:string[],
  requireAssignee:boolean,
  requireStoryPoints:boolean,
  requireSprint:boolean,
  requiredCustomFieldIds:string[],
  requiredSystemFields:string[],
  updatedAt:Timestamp
}

CustomFieldDefinition

{
  id:string,
  projectId:string,
  name:string,
  key:string,
  type:CustomFieldType,
  required:boolean,
  options:string[],
  position:int32,
  description:string,
  showOnCard:boolean
}

FieldPermission

{
  projectId:string,
  fieldKind:string,
  fieldId:string,
  viewRoleIds:string[],
  editRoleIds:string[],
  configured:boolean,
  canView:boolean,
  canEdit:boolean,
  selfView:boolean
}

fieldKind равен system, custom, personnel или calculation. configured:false означает открытый доступ для всех ролей. Для обычного участника canView и canEdit содержат уже вычисленный эффективный доступ, а списки ролей не раскрываются. Запись системного поля остаётся в fieldPermissions даже при canView:false, чтобы клиент мог убрать известный системный контрол из интерфейса; значение поля при этом не передаётся. selfView относится только к полям персонала и даёт участнику просмотр собственного значения. Пользователь с access.manage получает конфигурацию ролей и все определения полей, чтобы управлять матрицей доступа.

Wiki и автоматизации

WikiArticle

{
  id:string,
  projectId:string,
  parentId?:string,
  title:string,
  body:string,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

AutomationAction

{
  type:AutomationActionType,
  targetId?:string,
  value:string,
  config:map<string,string>,
  queryDsl:string,
  queryReferences:CalculationFormulaReference[],
  fieldMutation:AutomationFieldMutation
}

Для SET_ASSIGNEE можно передать либо фиксированный targetId, либо queryDsl вида select Сотрудник from Сотрудники … limit 1; одновременно задавать их нельзя. queryReferences сохраняют проверенную сервером привязку полей сотрудника и Карточка-контекста.

AutomationFieldMutation

{
  operation:AutomationFieldOperation,
  target:CalculationFormulaReference,
  formula:string,
  formulaReferences:CalculationFormulaReference[]
}

Используется только при AutomationAction.type = MUTATE_FIELD. target.source содержит стабильный ключ системного поля, custom:<FIELD_UUID> или путь к полю персонала: member_field:assignee:<PERSONNEL_FIELD_UUID> / member_field:<MEMBER_FIELD_UUID>:<PERSONNEL_FIELD_UUID>. Сервер проверяет права, существование и тип цели при сохранении правила. formulaReferences связывают подписи в формуле со стабильными источниками; переименование поля не ломает правило.

AutomationEventFilter

{
  actions:string[],
  statuses:string[],
  sourceBranchPattern:string,
  targetBranchPattern:string,
  refPattern:string,
  environmentPattern:string,
  jobNamePattern:string,
  labels:string[],
  changedFields:string[],
  conditionFormula:string,
  conditionFormulaReferences:CalculationFormulaReference[]
}

GitLab-триггеры используют поля события GitLab. Локальный ISSUE_UPDATED использует changedFields; пустой список принимает любое фактическое изменение, а непустой — любое совпавшее поле. Непустая conditionFormula дополнительно должна вернуть true; conditionFormulaReferences содержат её стабильные ссылки и исполняются тем же серверным движком выражений.

CountBoardFilters

  • Доступ: issue.view
  • Контракт: CountBoardFiltersRequest → CountBoardFiltersResponse
  • Request: {projectId:string, queries:BoardFilterCountQuery[]}
  • Response: {results:BoardFilterCountResult[]}

Считает несколько общих фильтров доски за один вызов и на одном снимке проекта. Каждый BoardFilterCountQuery содержит {key:string, query:string, references:CalculationFormulaReference[], cardFilter:BoardFilter, parameterValues:QueryParameterValue[]}. Сервер возвращает соответствующий BoardFilterCountResult в виде {key:string, totalCount:int32, error:string}; ошибка одного фильтра не отменяет корректные результаты остальных. Запросы этого метода должны использовать источник Карточки. За один вызов принимается не более 100 фильтров.

Результат DSL-запроса

QueryColumn

{label:string, source:string, type:string}

QueryValue

{raw:string, display:string, type:string, present:boolean}

QueryRow

{card:Issue, employee:Member, values:QueryValue[], swimlaneId:string}

swimlaneId связывает строку с одной из дорожек ответа. Классификация выполняется до пагинации, поэтому счётчики дорожек относятся ко всей выборке.

BoardSwimlaneResult

{
  id:string,
  name:string,
  color:string,
  totalCount:int32,
  unmatched:boolean,
  value:string
}

value используется интерфейсом для безопасного переноса карточки между дорожками, построенными по редактируемому полю. Для дорожек по правилам оно остаётся пустым.

QueryParameterValue

{name:string, value:string}

Значение параметра одного запуска. name принимается с начальным $ или без него; value может быть непосредственным значением нужного типа либо выражением вроде @lastMonth, last(14d) или range("2026-01-01", "2026-03-31").

QueryParameterDefinition

{
  name:string,
  type:string,
  valueType:string,
  defaultValue:string,
  resolved:QueryValue
}

Описывает объявленный параметр и его фактически разрешённое значение. type — тип управления (period, member, number и так далее), valueType — тип значения движка, а resolved учитывает переданное значение либо выражение по умолчанию.

QueryResolvedContext

{name:string, type:string, raw:string, display:string, present:boolean}

Фиксирует фактическое значение использованного системного контекста @… для конкретного запуска. Это позволяет интерфейсу показать, какие даты, сотрудник, спринт или календарь были применены.

QueryExplainStep

{kind:string, label:string, inputCount:int32, outputCount:int32}

Динамический шаг плана выполнения. inputCount и outputCount показывают реальное изменение количества строк на источнике, серверном фильтре, explode, where, группировке, distinct и limit.

RunQueryResponse

{
  columns:QueryColumn[],
  rows:QueryRow[],
  totalCount:int32,
  nextPageToken:string,
  references:CalculationFormulaReference[],
  normalizedQuery:string,
  parameters:QueryParameterDefinition[],
  resolvedContexts:QueryResolvedContext[],
  explain:QueryExplainStep[],
  swimlanes:BoardSwimlaneResult[]
}

RunQueryRequest содержит projectId, query, references, cardFilter, необязательный contextCardId, pageSize, pageToken и parameterValues:QueryParameterValue[]. Пустой entity в QueryRow не сериализуется protobuf JSON.

Пакетные счётчики доски

BoardFilterCountQuery {
  key:string,
  query:string,
  references:CalculationFormulaReference[],
  cardFilter:BoardFilter,
  parameterValues:QueryParameterValue[]
}

CountBoardFiltersRequest {
  projectId:string,
  queries:BoardFilterCountQuery[]
}

BoardFilterCountResult {
  key:string,
  totalCount:int32,
  error:string
}

CountBoardFiltersResponse {
  results:BoardFilterCountResult[]
}

AutomationRule

{
  id:string,
  projectId:string,
  name:string,
  enabled:boolean,
  trigger:AutomationTrigger,
  fromColumnId?:string,
  toColumnId?:string,
  integrationId?:string,
  eventFilter:AutomationEventFilter,
  actions:AutomationAction[],
  runCount:int64,
  lastRunAt?:Timestamp,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

В JSON runCount приходит строкой.

AutomationPreviewChange

{field:string, before:string, after:string}

field использует системные ключи column, assignee, priority, sprint, labels, blocked_reason, comment или custom:<FIELD_UUID>. Значения ID берутся из того же снимка проекта.

AutomationRun

{
  id:string,
  projectId:string,
  ruleId:string,
  ruleName:string,
  issueId:string,
  issueKey:string,
  trigger:AutomationTrigger,
  status:string,
  dryRun:boolean,
  actionCount:int32,
  changes:AutomationPreviewChange[],
  errorMessage:string,
  startedAt:Timestamp,
  finishedAt:Timestamp
}

Журнал хранит копию ruleName и issueKey, поэтому остаётся читаемым после удаления правила или карточки.

IssueTemplateFieldOption

{value:string, label:string}

IssueTemplateField

{
  key:string,
  label:string,
  description:string,
  mode:IssueTemplateFieldMode,
  defaultValues:string[],
  placeholder:string,
  position:int32,
  inputType:string,
  options:IssueTemplateFieldOption[]
}

IssueTemplate

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  enabled:boolean,
  access:IssueTemplateAccess,
  objectTypeId?:string,
  columnId?:string,
  fields:IssueTemplateField[],
  successMessage:string,
  shareUrl:string,
  expiresAt?:Timestamp,
  submissionLimit:int32,
  submissionCount:int64,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  accessGroupIds:string[],
  hasPassword:boolean
}

IssueTemplateForm

{
  templateId:string,
  name:string,
  description:string,
  projectName:string,
  projectColor:string,
  objectTypeName:string,
  fields:IssueTemplateField[],
  successMessage:string,
  requiresAuth:boolean,
  expiresAt?:Timestamp,
  access:IssueTemplateAccess,
  requiresPassword:boolean,
  accessGranted:boolean
}

IssueTemplateValue

{key:string, values:string[]}

IssueTemplateSubmission

{
  id:string,
  projectId:string,
  templateId:string,
  templateName:string,
  issueId:string,
  issueKey:string,
  idempotencyKey:string,
  submitterMemberId:string,
  status:string,
  errorMessage:string,
  submittedAt:Timestamp
}

submissionCount — protobuf int64, поэтому в JSON приходит строкой.

Рабочее время и сигналы

BusinessTimeInterval

{start:string, end:string}

start и end используют HH:MM; начало должно быть раньше конца.

BusinessDaySchedule

{weekday:int32, enabled:boolean, intervals:BusinessTimeInterval[]}

weekday использует ISO-нумерацию 1–7, где 1 — понедельник.

BusinessCalendarException

{date:string, name:string, working:boolean, intervals:BusinessTimeInterval[]}

dateYYYY-MM-DD. Для нерабочего исключения intervals пуст; для рабочего нужен хотя бы один интервал.

BusinessCalendar

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  timezone:string,
  schedule:BusinessDaySchedule[],
  exceptions:BusinessCalendarException[],
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

TimePolicy

{
  id:string,
  projectId:string,
  calendarId:string,
  name:string,
  description:string,
  enabled:boolean,
  startField:string,
  durationMinutes:int32,
  warningMinutes:int32,
  filter:BoardFilter,
  stopFilter:BoardFilter,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

TimePolicyEvaluation

{
  policyId:string,
  issueId:string,
  status:string,
  startedAt?:Timestamp,
  dueAt?:Timestamp,
  remainingMinutes:int32
}

remainingMinutes отрицателен после нарушения. statuson_track, warning, breached или stopped.

SignalRule

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  enabled:boolean,
  color:string,
  filter:BoardFilter,
  timePolicyId:string,
  policyStatuses:string[],
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

Signal

{
  id:string,
  projectId:string,
  ruleId:string,
  ruleName:string,
  issueId:string,
  issueKey:string,
  issueTitle:string,
  color:string,
  summary:string,
  status:string,
  timePolicyStatus:string,
  dueAt?:Timestamp,
  remainingMinutes:int32,
  acknowledgedBy:string,
  acknowledgedAt?:Timestamp
}

Signal вычисляется при чтении состояния; это не новый тип карточки и не добавляет системных полей в схему проекта.

Бизнес-действия

BusinessActionInputField

{
  key:string,
  label:string,
  description:string,
  type:string,
  required:boolean,
  options:string[],
  defaultValue:string
}

key — уникальный lower_snake_case. type принимает text, textarea, number, date, select, multiselect, checkbox, member или issue; options задают варианты для select и multiselect, а для member и issue могут ограничивать доступный каталог.

BusinessActionOperation

{
  field:string,
  value:string,
  inputKey:string,
  targetKind:string,
  memberSource:string
}

Если inputKey заполнен, значение берётся из ответа формы; иначе используется value.

targetKind = card изменяет карточку. В этом случае field принимает системную цель (column, assignee, priority, sprint, label:add, label:remove, title, description, issue_type, story_points, start_date, due_date, parent, blocked_reason, comment) либо custom:<FIELD_UUID>. Пустой targetKind читается как card для совместимости со старыми определениями.

targetKind = personnel изменяет поле профиля сотрудника с UUID из field. memberSource определяет сотрудника: assignee, actor, input:<FORM_FIELD_KEY> или custom:<MEMBER_FIELD_UUID>. Карточка и профиль сотрудника обновляются в одной транзакции: если хотя бы одно изменение недопустимо, не применяется ни одно.

BusinessAction

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  enabled:boolean,
  color:string,
  icon:string,
  confirmationText:string,
  filter:BoardFilter,
  inputFields:BusinessActionInputField[],
  operations:BusinessActionOperation[],
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

BusinessActionValue

{key:string, value:string}

BusinessActionRun

{
  id:string,
  projectId:string,
  actionId:string,
  actionName:string,
  issueId:string,
  issueKey:string,
  status:string,
  inputValues:BusinessActionValue[],
  changes:AutomationPreviewChange[],
  errorMessage:string,
  actorId:string,
  executedAt:Timestamp
}

Журнал хранит копии actionName и issueKey, поэтому остаётся читаемым после удаления определения или карточки.

Вычисляемые поля

CalculationOperand

{constant:boolean, field:string, value:string}

Для поля constant = false, а field содержит системный источник, custom:<FIELD_UUID> или calculated:<CALCULATION_UUID>. Для константы используется value; тип проверяется по оператору.

CalculationField

{
  id:string,
  projectId:string,
  name:string,
  key:string,
  description:string,
  enabled:boolean,
  color:string,
  objectTypeId?:string,
  kind:string,
  resultType:string,
  operator:string,
  operands:CalculationOperand[],
  relationTypeId:string,
  relationDirection:string,
  aggregateField:string,
  aggregation:string,
  relatedFilter:BoardFilter,
  decimalPlaces:int32,
  emptyPolicy:string,
  prefix:string,
  suffix:string,
  separator:string,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

resultType: number, text, date или boolean. emptyPolicy: empty, zero или error. Поддерживаются формулы copy, coalesce, sum, subtract, multiply, divide, percent, days_between, date_add_days, concat, all, any; rollup-агрегации count, count_distinct, sum, average, min, max, earliest, latest, first, concat.

CalculationValue

{fieldId:string, issueId:string, value:string, displayValue:string, error:string}

value предназначен для сравнений и последующих вычислений, displayValue уже содержит округление, префикс и суффикс. Ошибка относится только к конкретной паре поле–карточка и не блокирует чтение остального проекта.

Управляемые пакеты

BatchOperation

{field:string, value:string}

Поддерживаются те же системные и пользовательские цели, что у постоянных операций бизнес-действия. Источника из формы нет: значение всегда задано в определении пакета.

WorkBatch

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  enabled:boolean,
  color:string,
  selectionMode:string,
  filter:BoardFilter,
  issueIds:string[],
  executionMode:string,
  operations:BatchOperation[],
  maxItems:int32,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

filter и issueIds сохраняются одновременно, поэтому смена режима не уничтожает предыдущую конфигурацию состава. В динамическом режиме filter.queryDsl и filter.rules применяются совместно по AND; пустые значения не добавляют ограничений.

BatchItemResult

{
  issueId:string,
  issueKey:string,
  status:string,
  changes:AutomationPreviewChange[],
  errorMessage:string
}

WorkBatchPreview

{
  batchId:string,
  selectionHash:string,
  targetCount:int32,
  validCount:int32,
  invalidCount:int32,
  items:BatchItemResult[]
}

WorkBatchRun

{
  id:string,
  projectId:string,
  batchId:string,
  batchName:string,
  executionMode:string,
  selectionHash:string,
  status:string,
  targetCount:int32,
  successCount:int32,
  failureCount:int32,
  items:BatchItemResult[],
  actorId:string,
  startedAt:Timestamp,
  finishedAt:Timestamp
}

Маршрутизация

RoutingCandidate

{
  memberId:string,
  weight:int32,
  capacityMode:string,
  maxActive:int32
}

capacityMode выбирается явно: unlimited не ограничивает число подходящих карточек, max_active использует maxActive. weight влияет на least_loaded и weighted_cycle; порядок массива влияет на разрешение равенства, first_available и round_robin.

RoutingCandidateEvaluation

{
  memberId:string,
  eligible:boolean,
  activeCount:int32,
  maxActive:int32,
  weight:int32,
  score:double,
  reason:string
}

RoutingPolicy

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  enabled:boolean,
  color:string,
  triggers:AutomationTrigger[],
  manualEnabled:boolean,
  filter:BoardFilter,
  strategy:string,
  candidates:RoutingCandidate[],
  loadFilter:BoardFilter,
  fallbackMode:string,
  fallbackMemberId:string,
  terminal:boolean,
  failureMode:string,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

fallbackMode: keep_current, clear, member или reject. failureMode: continue или block. Фильтры и неактивные ссылки сохраняются при выключении возможности.

RoutingPreview

{
  policyId:string,
  issueId:string,
  issueKey:string,
  applicable:boolean,
  selectedMemberId:string,
  fallbackUsed:boolean,
  decision:string,
  reason:string,
  selectionHash:string,
  candidates:RoutingCandidateEvaluation[]
}

RoutingRun

{
  id:string,
  projectId:string,
  policyId:string,
  policyName:string,
  issueId:string,
  issueKey:string,
  trigger:AutomationTrigger,
  automatic:boolean,
  status:string,
  selectedMemberId:string,
  fallbackUsed:boolean,
  decision:string,
  reason:string,
  actorId:string,
  startedAt:Timestamp,
  finishedAt:Timestamp
}

Операционные дашборды

DashboardWidget

{
  id:string,
  name:string,
  description:string,
  color:string,
  visualization:string,
  filter:BoardFilter,
  measureField:string,
  aggregation:string,
  groupByField:string,
  timeMode:string,
  dateField:string,
  windowDays:int32,
  startDate:string,
  endDate:string,
  interval:string,
  order:string,
  limit:int32,
  tableFields:string[],
  width:int32
}

visualization: number, breakdown, trend или table. aggregation: count, count_distinct, sum, average, min или max. timeMode: all_time, rolling_days или date_range; для динамики interval выбирается между day, week и month. Поля могут быть системными, пользовательскими (custom:<id>), вычисляемыми (calculated:<id>) или связями (relation:<id>).

Dashboard

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  enabled:boolean,
  color:string,
  scopeFilter:BoardFilter,
  widgets:DashboardWidget[],
  refreshMode:string,
  refreshSeconds:int32,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

DashboardPoint

{
  key:string,
  label:string,
  value:double,
  issueCount:int32,
  issueIds:string[]
}

DashboardCell

{field:string, label:string, value:string}

DashboardRow

{issueId:string, issueKey:string, cells:DashboardCell[]}

DashboardWidgetResult

{
  widgetId:string,
  status:string,
  error:string,
  value:double,
  issueCount:int32,
  points:DashboardPoint[],
  rows:DashboardRow[],
  issueIds:string[]
}

DashboardPreview

{
  dashboardId:string,
  widgets:DashboardWidgetResult[],
  evaluatedAt:Timestamp
}

Интеграции и агенты

YouGileCompany

{id:string, name:string, isAdmin:boolean}

YouGileProject

{id:string, title:string, taskCount?:int32, archivedTaskCount?:int32}

YouGileBoard

{id:string, title:string, projectId:string, taskCount?:int32, archivedTaskCount?:int32}

IntegrationConnection

{
  id:string,
  projectId:string,
  provider:IntegrationProvider,
  name:string,
  baseUrl:string,
  externalProjectId:string,
  query:string,
  username:string,
  enabled:boolean,
  hasSecret:boolean,
  hasWebhookSecret:boolean,
  status:string,
  syncProgress:string,
  lastError:string,
  lastSyncAt?:Timestamp,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  webhookUrl:string
}

IntegrationRunResult

{
  scanned:int32,
  created:int32,
  updated:int32,
  skipped:int32,
  users:int32,
  checklists:int32,
  checklistItems:int32,
  parentLinks:int32,
  dependencies:int32,
  relations:int32,
  chatsQueued:int32,
  errors:string[]
}

GitLabWebhookEvent

{
  id:string,
  projectId:string,
  integrationId:string,
  integrationName:string,
  eventUuid:string,
  eventType:string,
  action:string,
  status:string,
  ref:string,
  sourceBranch:string,
  targetBranch:string,
  environment:string,
  jobName:string,
  externalId:string,
  issueId:string,
  issueKey:string,
  processingStatus:string,
  matchedRuleCount:int32,
  errorMessage:string,
  receivedAt:Timestamp,
  processedAt?:Timestamp
}

GitLabOutboxAction

{
  id:string,
  projectId:string,
  ruleId:string,
  ruleName:string,
  integrationId:string,
  integrationName:string,
  issueId:string,
  issueKey:string,
  actionType:AutomationActionType,
  config:map<string,string>,
  status:string,
  attempts:int32,
  errorMessage:string,
  resultJson:string,
  nextAttemptAt?:Timestamp,
  createdAt:Timestamp,
  startedAt?:Timestamp,
  finishedAt?:Timestamp
}

Webhook-журнал дедуплицируется по eventUuid (при отсутствии — по hash payload). Outbox сохраняет снимки названий, параметров и результата; после ошибки использует ограниченный exponential backoff, а окончательно failed-действие можно вернуть через RetryGitLabOutboxAction.

AgentToken

{
  id:string,
  workspaceId:string,
  memberId:string,
  agentId:string,
  name:string,
  prefix:string,
  scopes:string[],
  expiresAt:Timestamp,
  lastUsedAt?:Timestamp,
  createdAt:Timestamp
}

AgentCapabilityPolicy

{capability:string, decision:string}

capability — один из read, write, admin; decisionallow, ask или deny.

Agent

{
  id:string,
  workspaceId:string,
  memberId:string,
  ownerMemberId:string,
  name:string,
  description:string,
  status:string,
  runtime:string,
  model:string,
  capabilities:AgentCapabilityPolicy[],
  configVersion:int32,
  lastActiveAt?:Timestamp,
  createdAt:Timestamp,
  updatedAt:Timestamp,
  member:Member,
  owner:Member,
  projectRoleIds:string[],
  tokens:AgentToken[]
}

AgentApprovalReceipt

{
  id:string,
  workspaceId:string,
  projectId:string,
  agentId:string,
  agentMemberId:string,
  ownerMemberId:string,
  agentName:string,
  tokenId:string,
  tokenPrefix:string,
  tokenName:string,
  agentConfigVersion:int32,
  procedure:string,
  capability:string,
  requestHash:string,
  requestJson:string,
  entityType:string,
  entityId:string,
  summary:string,
  beforeJson:string,
  afterJson:string,
  stateHash:string,
  risk:string,
  reversible:boolean,
  status:string,
  reviewedByMemberId:string,
  reviewer?:Member,
  reviewComment:string,
  failureMessage:string,
  requestedAt:Timestamp,
  expiresAt:Timestamp,
  reviewedAt?:Timestamp,
  executionStartedAt?:Timestamp,
  executedAt?:Timestamp,
  updatedAt:Timestamp
}

status проходит жизненный цикл pending → approved → executing → executed; альтернативные исходы — rejected, failed, expired. requestJson, beforeJson и afterJson предназначены для human review и не используются как команда на исполнение. Авторитетная привязка — requestHash, а stale-state защита — stateHash.

AgentRun

{
  id:string,
  workspaceId:string,
  projectId:string,
  agentId:string,
  agentMemberId:string,
  ownerMemberId:string,
  agentName:string,
  runtime:string,
  model:string,
  tokenId:string,
  tokenPrefix:string,
  tokenName:string,
  agentConfigVersion:int32,
  procedure:string,
  capability:string,
  policyDecision:string,
  requestHash:string,
  requestJson:string,
  entityType:string,
  entityId:string,
  clientName:string,
  approvalId:string,
  status:string,
  connectCode:string,
  errorMessage:string,
  responseBytes:int64,
  durationMs:int64,
  delegatedByMemberId:string,
  agentSkillInvocationId:string,
  startedAt:Timestamp,
  finishedAt?:Timestamp
}

requestJson и errorMessage маскируют секретные поля исходного protobuf request; requestHash остаётся привязан к полному запросу. responseBytes — размер protobuf message, не wire-size HTTP/Connect framing. Для managed delegation agentMemberId сохраняет исполняющего Agent, delegatedByMemberId — человека, чьи права применены, а agentSkillInvocationId — источник делегации. Terminal run не переиспользуется и не меняется повторным вызовом.

AgentTool

{name:string, description:string, requiredScope:string, readOnly:boolean, destructive:boolean}

AgentManifest

{
  service:string,
  version:string,
  protocol:string,
  baseUrl:string,
  apiDocsPath:string,
  mcpCommand:string,
  scopes:string[],
  tools:AgentTool[]
}

Снимок проекта

ProjectState

{
  project:Project,
  columns:BoardColumn[],
  sprints:Sprint[],
  issues:Issue[],
  labels:Label[],
  activities:ActivityEvent[],
  savedBoards:SavedBoard[],
  workflowRules:WorkflowRule[],
  customFields:CustomFieldDefinition[],
  automationRules:AutomationRule[],
  integrations:IntegrationConnection[],
  boardFilterCards:BoardFilterCard[],
  wikiArticles:WikiArticle[],
  objectTypes:ObjectType[],
  objectTypeFields:ObjectTypeField[],
  relationTypes:RelationType[],
  objectRelations:ObjectRelation[],
  capabilities:ProjectCapability[],
  automationRuns:AutomationRun[],
  businessCalendars:BusinessCalendar[],
  personnelFields:PersonnelFieldDefinition[],
  memberProfiles:MemberProjectProfile[],
  timePolicies:TimePolicy[],
  timePolicyEvaluations:TimePolicyEvaluation[],
  signalRules:SignalRule[],
  signals:Signal[],
  businessActions:BusinessAction[],
  businessActionRuns:BusinessActionRun[],
  calculationFields:CalculationField[],
  calculationValues:CalculationValue[],
  workBatches:WorkBatch[],
  workBatchRuns:WorkBatchRun[],
  routingPolicies:RoutingPolicy[],
  routingRuns:RoutingRun[],
  dashboards:Dashboard[],
  issueTemplates:IssueTemplate[],
  issueTemplateSubmissions:IssueTemplateSubmission[],
  gitlabWebhookEvents:GitLabWebhookEvent[],
  gitlabOutboxActions:GitLabOutboxAction[],
  accessGroups:AccessGroup[],
  projectRoles:ProjectRole[],
  memberProjectRoles:MemberProjectRole[],
  permissionCatalog:PermissionDefinition[],
  currentPermissions:string[],
  fieldPermissions:FieldPermission[],
  boardSubscriptions:BoardSubscription[],
  boardNotifications:BoardNotification[],
  notifications:MemberNotification[]
}

Отчёты

GetReportsResponse

{
  reports:Report[]
}

Report

{
  id:string,
  projectId:string,
  name:string,
  description:string,
  enabled:bool,
  color:string,
  visualization:string,
  queryDsl:string,
  queryReferences:CalculationFormulaReference[],
  systemKey:string,
  position:int32,
  createdAt:Timestamp,
  updatedAt:Timestamp
}

PreviewReportRequest

{
  reportId:string,
  parameterValues:QueryParameterValue[]
}

ReportPreview

{
  reportId:string,
  columns:QueryColumn[],
  rows:QueryRow[],
  totalCount:int32,
  references:CalculationFormulaReference[],
  normalizedQuery:string,
  evaluatedAt:Timestamp,
  parameters:QueryParameterDefinition[],
  resolvedContexts:QueryResolvedContext[],
  explain:QueryExplainStep[]
}

Enum-справочник

MemberRole

Имя Значение
MEMBER_ROLE_UNSPECIFIED 0
MEMBER_ROLE_OWNER 1
MEMBER_ROLE_EDITOR 2
MEMBER_ROLE_VIEWER 3

ColumnCategory

Имя Значение
COLUMN_CATEGORY_UNSPECIFIED 0
COLUMN_CATEGORY_TODO 1
COLUMN_CATEGORY_IN_PROGRESS 2
COLUMN_CATEGORY_DONE 3

RelationCardinality

Имя Значение
RELATION_CARDINALITY_UNSPECIFIED 0
RELATION_CARDINALITY_MANY_TO_MANY 1
RELATION_CARDINALITY_ONE_TO_MANY 2
RELATION_CARDINALITY_MANY_TO_ONE 3
RELATION_CARDINALITY_ONE_TO_ONE 4

IssueType

Имя Значение
ISSUE_TYPE_UNSPECIFIED 0
ISSUE_TYPE_TASK 1
ISSUE_TYPE_STORY 2
ISSUE_TYPE_BUG 3

IssuePriority

Имя Значение
ISSUE_PRIORITY_UNSPECIFIED 0
ISSUE_PRIORITY_LOW 1
ISSUE_PRIORITY_NORMAL 2
ISSUE_PRIORITY_HIGH 3
ISSUE_PRIORITY_CRITICAL 4

TimelineDependencyType

Имя Значение Смысл
TIMELINE_DEPENDENCY_TYPE_UNSPECIFIED 0 Не задано; сервер ожидает явный тип
TIMELINE_DEPENDENCY_TYPE_FINISH_START 1 Окончание предшественника управляет началом последователя
TIMELINE_DEPENDENCY_TYPE_START_START 2 Начало управляет началом
TIMELINE_DEPENDENCY_TYPE_FINISH_FINISH 3 Окончание управляет окончанием
TIMELINE_DEPENDENCY_TYPE_START_FINISH 4 Начало управляет окончанием

SprintStatus

Имя Значение
SPRINT_STATUS_UNSPECIFIED 0
SPRINT_STATUS_PLANNED 1
SPRINT_STATUS_ACTIVE 2
SPRINT_STATUS_COMPLETED 3

CustomFieldType

Имя Значение
CUSTOM_FIELD_TYPE_UNSPECIFIED 0
CUSTOM_FIELD_TYPE_TEXT 1
CUSTOM_FIELD_TYPE_NUMBER 2
CUSTOM_FIELD_TYPE_DATE 3
CUSTOM_FIELD_TYPE_SELECT 4
CUSTOM_FIELD_TYPE_CHECKBOX 5
CUSTOM_FIELD_TYPE_MEMBER 6
CUSTOM_FIELD_TYPE_WIKI_ARTICLE 7
CUSTOM_FIELD_TYPE_HOURS 8

BoardFilterOperator

Имя Значение
BOARD_FILTER_OPERATOR_UNSPECIFIED 0
BOARD_FILTER_OPERATOR_EQUALS 1
BOARD_FILTER_OPERATOR_NOT_EQUALS 2
BOARD_FILTER_OPERATOR_CONTAINS 3
BOARD_FILTER_OPERATOR_NOT_CONTAINS 4
BOARD_FILTER_OPERATOR_GREATER_THAN 5
BOARD_FILTER_OPERATOR_LESS_THAN 6
BOARD_FILTER_OPERATOR_IS_EMPTY 7
BOARD_FILTER_OPERATOR_IS_NOT_EMPTY 8

BoardSwimlaneMode

Имя Значение
BOARD_SWIMLANE_MODE_UNSPECIFIED 0
BOARD_SWIMLANE_MODE_FIELD 1
BOARD_SWIMLANE_MODE_RULES 2

AutomationTrigger

Имя Значение
AUTOMATION_TRIGGER_UNSPECIFIED 0
AUTOMATION_TRIGGER_ISSUE_MOVED 1
AUTOMATION_TRIGGER_ISSUE_CREATED 2
AUTOMATION_TRIGGER_ISSUE_UPDATED 3
AUTOMATION_TRIGGER_GITLAB_MERGE_REQUEST 4
AUTOMATION_TRIGGER_GITLAB_PIPELINE 5
AUTOMATION_TRIGGER_GITLAB_JOB 6
AUTOMATION_TRIGGER_GITLAB_PUSH 7
AUTOMATION_TRIGGER_GITLAB_TAG 8
AUTOMATION_TRIGGER_GITLAB_DEPLOYMENT 9
AUTOMATION_TRIGGER_GITLAB_RELEASE 10
AUTOMATION_TRIGGER_GITLAB_NOTE 11

AutomationActionType

Имя Значение
AUTOMATION_ACTION_TYPE_UNSPECIFIED 0
AUTOMATION_ACTION_TYPE_MOVE_COLUMN 1
AUTOMATION_ACTION_TYPE_SET_ASSIGNEE 2
AUTOMATION_ACTION_TYPE_SET_PRIORITY 3
AUTOMATION_ACTION_TYPE_SET_SPRINT 4
AUTOMATION_ACTION_TYPE_ADD_LABEL 5
AUTOMATION_ACTION_TYPE_SET_CUSTOM_FIELD 6
AUTOMATION_ACTION_TYPE_APPLY_BOARD 7
AUTOMATION_ACTION_TYPE_SET_BLOCKED_REASON 8
AUTOMATION_ACTION_TYPE_ADD_COMMENT 9
AUTOMATION_ACTION_TYPE_MUTATE_FIELD 10
AUTOMATION_ACTION_TYPE_GITLAB_CREATE_BRANCH 20
AUTOMATION_ACTION_TYPE_GITLAB_CREATE_MERGE_REQUEST 21
AUTOMATION_ACTION_TYPE_GITLAB_UPDATE_MERGE_REQUEST 22
AUTOMATION_ACTION_TYPE_GITLAB_ADD_NOTE 23
AUTOMATION_ACTION_TYPE_GITLAB_APPROVE_MERGE_REQUEST 24
AUTOMATION_ACTION_TYPE_GITLAB_UNAPPROVE_MERGE_REQUEST 25
AUTOMATION_ACTION_TYPE_GITLAB_MERGE_MERGE_REQUEST 26
AUTOMATION_ACTION_TYPE_GITLAB_CLOSE_MERGE_REQUEST 27
AUTOMATION_ACTION_TYPE_GITLAB_REOPEN_MERGE_REQUEST 28
AUTOMATION_ACTION_TYPE_GITLAB_RUN_PIPELINE 29
AUTOMATION_ACTION_TYPE_GITLAB_RETRY_PIPELINE 30
AUTOMATION_ACTION_TYPE_GITLAB_CANCEL_PIPELINE 31
AUTOMATION_ACTION_TYPE_GITLAB_PLAY_JOB 32
AUTOMATION_ACTION_TYPE_GITLAB_RETRY_JOB 33
AUTOMATION_ACTION_TYPE_GITLAB_CANCEL_JOB 34
AUTOMATION_ACTION_TYPE_GITLAB_LINK_MERGE_REQUEST 35

AutomationFieldOperation

Имя Значение
AUTOMATION_FIELD_OPERATION_UNSPECIFIED 0
AUTOMATION_FIELD_OPERATION_SET 1
AUTOMATION_FIELD_OPERATION_INC 2
AUTOMATION_FIELD_OPERATION_DEC 3

IssueTemplateAccess

Имя Значение
ISSUE_TEMPLATE_ACCESS_UNSPECIFIED 0
ISSUE_TEMPLATE_ACCESS_MEMBERS 1
ISSUE_TEMPLATE_ACCESS_PUBLIC 2
ISSUE_TEMPLATE_ACCESS_PASSWORD 3
ISSUE_TEMPLATE_ACCESS_GROUPS 4

IssueTemplateFieldMode

Имя Значение
ISSUE_TEMPLATE_FIELD_MODE_UNSPECIFIED 0
ISSUE_TEMPLATE_FIELD_MODE_HIDDEN 1
ISSUE_TEMPLATE_FIELD_MODE_EDITABLE 2
ISSUE_TEMPLATE_FIELD_MODE_REQUIRED 3
ISSUE_TEMPLATE_FIELD_MODE_LOCKED 4

IntegrationProvider

Имя Значение
INTEGRATION_PROVIDER_UNSPECIFIED 0
INTEGRATION_PROVIDER_JIRA 1
INTEGRATION_PROVIDER_YOUGILE 2
INTEGRATION_PROVIDER_GITLAB 3

Дополнительные HTTP endpoints

Эти пути не входят в BoardService.

Метод и путь Авторизация Назначение
GET /livez нет Процесс запущен
GET /readyz нет API запущен и база отвечает
GET /docs/api.md нет Этот документ
GET /docs/agent-guide.md нет Руководство для ИИ-агента
GET /.well-known/algoboard-agent.json нет Agent manifest
POST /webhooks/gitlab/{integration_id} X-Gitlab-Token GitLab merge request webhook

Максимальный размер HTTP-запроса — 2 МиБ. GitLab webhook отвечает 202 {"ok":true} после принятия события. Ошибки: 400 — некорректное тело, 401 — неверный secret, 404 — подключение не найдено.

ChatService

Чаты используют отдельный endpoint POST /algoboard.v1.ChatService/{Method} и не входят в тяжёлый снимок GetAppState. Каждый запрос проходит session, workspace, capability chats, проектные разрешения и membership разговора. Мутации сообщений, каналов и состава DM повторяют эти проверки по свежим данным уже после получения conversation lock, поэтому параллельный отзыв права, capability или архивирование проекта не оставляет окно stale authorization. Приватный недоступный разговор отвечает not_found без раскрытия его существования.

message.sequence задаёт неизменяемый порядок сообщений. eventSequence отдельно упорядочивает создание, редактирование, удаление и изменения состава; после reconnect клиент вызывает ListConversationEvents, а WebSocket использует только как сигнал.

P0 rate limiter хранится в памяти процесса и использует отдельные token buckets для каждой тройки workspaceId + memberId + action: SendMessage допускает burst 20 и восстанавливает один token каждые 500 мс, EditMessage — burst 10 и один token каждую секунду. Превышение отвечает resource_exhausted с публичным сообщением и metadata Retry-After; рестарт процесса очищает buckets.

GetChatBootstrap

GetChatBootstrapRequest → GetChatBootstrapResponse. Поля запроса: workspaceId, projectId. Возвращает currentMember, chatsEnabled, permissions, первую страницу conversations, её nextCursor, авторитетный unreadConversationCount и serverTime.

ListConversations

ListConversationsRequest → ListConversationsResponse. Поля запроса: workspaceId, projectId, непрозрачный cursor, limit, includeArchived. Ответ: conversations, nextCursor.

CreateChannel

CreateChannelRequest → CreateChannelResponse. Поля запроса: workspaceId, projectId, name, description, visibility, memberIds. Ответ содержит conversation.

UpdateChannel

UpdateChannelRequest → UpdateChannelResponse. Поля запроса: projectId, conversationId, optional name, description, visibility. Ответ содержит conversation.

ArchiveChannel

ArchiveChannelRequest → ArchiveChannelResponse. Поля запроса: projectId, conversationId. Ответ содержит conversation.

AddChannelMembers

AddChannelMembersRequest → AddChannelMembersResponse. Поля запроса: projectId, conversationId, memberIds. Ответ содержит conversation.

RemoveChannelMember

RemoveChannelMemberRequest → RemoveChannelMemberResponse. Поля запроса: projectId, conversationId, memberId. Ответ содержит conversation.

OpenDirectConversation

OpenDirectConversationRequest → OpenDirectConversationResponse. Поля запроса: workspaceId, projectId, participantMemberIds, clientRequestId. Ответ содержит conversation и created; повторное открытие той же пары возвращает существующий DM.

AddDirectParticipants

AddDirectParticipantsRequest → AddDirectParticipantsResponse. Поля запроса: projectId, conversationId, memberIds, обязательный confirmHistoryAccess. Ответ содержит тот же conversation и systemMessage. Новый участник видит историю с первого sequence, но сервер ставит его read cursor на системное событие добавления.

LeaveDirectConversation

LeaveDirectConversationRequest → LeaveDirectConversationResponse. Поля запроса: projectId, conversationId. Пустой ответ подтверждает отзыв дальнейшего доступа.

ListMessages

ListMessagesRequest → ListMessagesResponse. Поля запроса: projectId, conversationId, optional beforeSequence или afterSequence, limit. Ответ: messages, hasMore; offset-пагинации нет.

GetMessage

GetMessageRequest → GetMessageResponse. Поля запроса: projectId, conversationId, messageId. Возвращает точный актуальный snapshot message, включая soft-deleted сообщение и threadRootId; применяет тот же project/chat/membership access, что и ListMessages.

SendMessage

SendMessageRequest → SendMessageResponse. Поля запроса: projectId, conversationId, уникальный clientRequestId, структурированный content, optional threadRootId. Ответ содержит message и duplicate; повтор одного client request ID не создаёт вторую запись и не повторяет membership/notification side effects. Обычный текст человека в личном разговоре ровно с одним активным Agent автоматически создаёт идемпотентный direct-chat invocation; slash-команда остаётся в отдельном InvokeAgentSkill, групповой DM и сообщение самого Agent автозапуск не создают. Успешный FinishAgentSkillInvocation публикует результат direct-chat запуска обратно в тот же разговор и тред от имени Agent. Первая новая отправка в открытый проектный канал атомарно создаёт или реактивирует явный membership отправителя с ролью MEMBER и уровнем уведомлений all; durable chat.membership.updated предшествует chat.message.created, но invite-уведомление, системное сообщение и audit добавления не создаются. Delayed replay после более позднего удаления membership возвращает исходное сообщение и не вступает в канал заново.

EditMessage

EditMessageRequest → EditMessageResponse. Поля запроса: projectId, conversationId, messageId, content. Ответ содержит обновлённый message.

DeleteMessage

DeleteMessageRequest → DeleteMessageResponse. Поля запроса: projectId, conversationId, messageId, moderatorReason. Ответ содержит soft-deleted message; модераторское удаление требует причины и попадает в аудит.

ListThreadReplies

ListThreadRepliesRequest → ListThreadRepliesResponse. Поля запроса: projectId, conversationId, threadRootId, optional beforeSequence или afterSequence, limit. Ответ: rootMessage, replies, hasMore.

MarkConversationRead

MarkConversationReadRequest → MarkConversationReadResponse. Поля запроса: projectId, conversationId, lastReadSequence, optional observedThreadRootId, optional observedFromSequence. readCursorScope = GLOBAL_CONVERSATION_SEQUENCE: один cursor упорядочивает корневые сообщения и ответы всех тредов. observedFromSequence — sequence самого раннего реально загруженного сообщения наблюдаемой страницы; без этого поля сервер безопасно не продвигает cursor. Для основной ленты клиент не передаёт observedThreadRootId: наблюдаемыми считаются только top-level сообщения с sequence >= observedFromSequence, а replies и более ранняя незагруженная история блокируют продвижение. Для открытого треда клиент передаёт ID корня: корень считается загруженным отдельно, а среди replies наблюдаемы только записи этого root с sequence >= observedFromSequence; более ранний reply, top-level сообщение или reply другого треда остаётся blocker. SendMessage и системные сообщения сами по себе cursor не продвигают: после рендера клиент вызывает этот RPC с фактической границей загруженной страницы. Исключение — явная инициализация cursor нового участника DM на системном событии добавления.

Ответ возвращает фактически сохранённый lastReadSequence, авторитетный unreadMentionCount, readCursorScope и упорядоченные по firstUnreadSequence unreadThreadSummaries. Сервер возвращает не более 50 самых ранних непрочитанных roots на разговор; unreadThreadRootCount содержит полное число непрочитанных roots, а unreadThreadSummariesTruncated сообщает, что список усечён. Каждая запись содержит rootMessageId, firstUnreadSequence, lastUnreadSequence, unreadCount и присутствует даже тогда, когда root не вошёл в текущую страницу ListMessages. После продвижения cursor ответ показывает следующую группу ранних roots. Клиент использует rootMessageId, чтобы загрузить тред, и всегда продвигает cursor по порядку. Точный server-side blocker не ограничен этими 50 summary и продолжает учитывать все сообщения. chat.read.updated создаётся и публикуется только при фактическом увеличении cursor; повторный, заблокированный или вызванный без границы mark не раздувает durable event log.

SearchDirectParticipants

SearchDirectParticipantsRequest → SearchDirectParticipantsResponse. Поля запроса: projectId, query, limit. Требует project.view, chat.view, chat.send и capability chats; возвращает активных участников workspace без текущего пользователя. Поиск учитывает имя и email, но ответ раскрывает только безопасные поля memberId, displayName, avatarColor.

SearchChannelParticipants

SearchChannelParticipantsRequest → SearchChannelParticipantsResponse. Поля запроса: projectId, query, limit. Требует project.view, chat.view, chat.channel.manage и capability chats; возвращает активных участников workspace в минимальной проекции memberId, displayName, avatarColor, без email и account login.

SearchMentionableMembers

SearchMentionableMembersRequest → SearchMentionableMembersResponse. Поля запроса: projectId, conversationId, query, limit. Ответ содержит доступных для упоминания members; клиент сохраняет выбранный memberId, а не имя.

SearchMentionableIssues

SearchMentionableIssuesRequest → SearchMentionableIssuesResponse. Поля запроса: projectId, conversationId, query, limit. Ответ содержит доступные issues; клиент сохраняет issueId, а не key snapshot.

ListConversationEvents

ListConversationEventsRequest → ListConversationEventsResponse. Поля запроса: projectId, conversationId, afterEventSequence, limit. Ответ: durable events, hasMore. Broadcast-события доступны всем текущим читателям разговора; targeted-типы chat.notification.created и chat.read.updated возвращаются только когда memberId совпадает с текущим участником, как и при live WebSocket-доставке.

ChatConversationType

Значения: CHAT_CONVERSATION_TYPE_CHANNEL, CHAT_CONVERSATION_TYPE_DIRECT.

ChatConversationVisibility

Значения: CHAT_CONVERSATION_VISIBILITY_PROJECT_OPEN, CHAT_CONVERSATION_VISIBILITY_PRIVATE.

ChatConversationMemberRole

Значения: OWNER, MODERATOR, MEMBER в пространстве имён enum.

ChatReadCursorScope

Значение CHAT_READ_CURSOR_SCOPE_GLOBAL_CONVERSATION_SEQUENCE означает единый монотонный cursor по общей последовательности корневых сообщений и replies. CHAT_READ_CURSOR_SCOPE_UNSPECIFIED не используется серверным ответом.

ChatMessageKind

Значения: пользовательское сообщение, системное добавление и системное удаление участника.

Модели чатов

Модель Поля protobuf JSON
ChatMemberMention memberId, labelSnapshot
ChatIssueReference issueId, неизменяемый projectId, отображаемый snapshot keySnapshot, response-only текущие title и state; сервер игнорирует входные title/state, не сохраняет их и пакетно вычисляет заново при чтении; если читатель потерял issue.view, представление становится обычным text #keySnapshot без ID, title и state, а сохранённый content не меняется
ChatIssueSourceInput conversationId, messageId; optional-вложение CreateIssueRequest.chatSource для атомарного создания карточки из сообщения
ChatContentPart oneof text, memberMention, issueReference
ChatMessageContent version, parts
ChatConversationMember conversationId, memberId, role, member, joinedAt, leftAt
ChatConversationActionFlags canView, canSend, canManage, canModerate, canAddParticipants, canLeave
ChatConversation id, workspaceId, projectId, type, visibility, name, description, createdBy, members, lastSequence, eventSequence, lastReadSequence, unreadCount, hasUnreadMention, lastActivityAt, createdAt, updatedAt, archivedAt, actions, unreadThreadSummaries, readCursorScope, unreadThreadSummariesTruncated, unreadThreadRootCount
ChatThreadSummary rootMessageId, replyCount, participantMemberIds, lastReplyAt, lastReplySequence
ChatUnreadThreadSummary rootMessageId, firstUnreadSequence, lastUnreadSequence, unreadCount
ChatMessageActionFlags canEdit, canDelete, canReply, canPromoteToIssue; последнее вычисляется сервером из типа/состояния сообщения, отсутствия существующей связи и права issue.create
ChatMessage id, conversationId, sequence, authorId, author, kind, threadRootId, content, plainText, clientRequestId, createdAt, editedAt, deletedAt, threadSummary, actions, optional permission-aware promotedIssue с текущей карточкой
ChatConversationEvent id, workspaceId, conversationId, eventSequence, type, messageId, conversationSequence, memberId, actorId, payloadJson, publishedAt
GetChatBootstrapRequest workspaceId, projectId
GetChatBootstrapResponse workspaceId, projectId, currentMember, chatsEnabled, permissions, conversations, nextCursor, unreadConversationCount, serverTime
ListConversationsRequest workspaceId, projectId, cursor, limit, includeArchived
ListConversationsResponse conversations, nextCursor
CreateChannelRequest workspaceId, projectId, name, description, visibility, memberIds
CreateChannelResponse conversation
UpdateChannelRequest projectId, conversationId, name, description, visibility
UpdateChannelResponse conversation
ArchiveChannelRequest projectId, conversationId
ArchiveChannelResponse conversation
AddChannelMembersRequest projectId, conversationId, memberIds
AddChannelMembersResponse conversation
RemoveChannelMemberRequest projectId, conversationId, memberId
RemoveChannelMemberResponse conversation
OpenDirectConversationRequest workspaceId, projectId, participantMemberIds, clientRequestId
OpenDirectConversationResponse conversation, created
AddDirectParticipantsRequest projectId, conversationId, memberIds, confirmHistoryAccess
AddDirectParticipantsResponse conversation, systemMessage
LeaveDirectConversationRequest projectId, conversationId
LeaveDirectConversationResponse без полей
ListMessagesRequest projectId, conversationId, beforeSequence, afterSequence, limit
ListMessagesResponse messages, hasMore
GetMessageRequest projectId, conversationId, messageId
GetMessageResponse message
SendMessageRequest projectId, conversationId, clientRequestId, content, threadRootId
SendMessageResponse message, duplicate
EditMessageRequest projectId, conversationId, messageId, content
EditMessageResponse message
DeleteMessageRequest projectId, conversationId, messageId, moderatorReason
DeleteMessageResponse message
ListThreadRepliesRequest projectId, conversationId, threadRootId, beforeSequence, afterSequence, limit
ListThreadRepliesResponse rootMessage, replies, hasMore
MarkConversationReadRequest projectId, conversationId, lastReadSequence, observedThreadRootId, observedFromSequence
MarkConversationReadResponse lastReadSequence, unreadThreadSummaries, readCursorScope, unreadThreadSummariesTruncated, unreadThreadRootCount, unreadMentionCount
ChatDirectParticipant memberId, displayName, avatarColor
SearchDirectParticipantsRequest projectId, query, limit
SearchDirectParticipantsResponse participants
SearchChannelParticipantsRequest projectId, query, limit
SearchChannelParticipantsResponse participants
SearchMentionableMembersRequest projectId, conversationId, query, limit
SearchMentionableMembersResponse members
ChatIssueSuggestion issueId, projectId, key, title, state, assigneeId, archived
SearchMentionableIssuesRequest projectId, conversationId, query, limit
SearchMentionableIssuesResponse issues
ListConversationEventsRequest projectId, conversationId, afterEventSequence, limit
ListConversationEventsResponse events, hasMore

MemberNotification дополнен полями targetType, conversationId, messageId, threadRootId; старые comment targets продолжают использовать projectId, issueId и commentId.

Примеры клиентов

TypeScript, Connect-Web

Сгенерированный клиент уже находится в src/api/generated.

import { createClient } from '@connectrpc/connect'
import { createConnectTransport } from '@connectrpc/connect-web'
import { BoardService } from './generated/algoboard/v1/algoboard_pb'

const transport = createConnectTransport({
  baseUrl: 'http://localhost:8080',
  fetch: (input, init) => fetch(input, {
    ...init,
    credentials: 'include',
  }),
})

const client = createClient(BoardService, transport)
const state = await client.getAppState({})

Для Bearer-токена добавьте interceptor или передавайте headers в call options:

const state = await client.getAppState({}, {
  headers: {
    Authorization: `Bearer ${token}`,
  },
})

Go, ConnectRPC

client := algoboardv1connect.NewBoardServiceClient(
    http.DefaultClient,
    "http://localhost:8080",
)

request := connect.NewRequest(&algoboardv1.GetAppStateRequest{})
request.Header().Set("Authorization", "Bearer "+token)

response, err := client.GetAppState(ctx, request)
if err != nil {
    return err
}
fmt.Println(response.Msg.GetProjectState().GetProject().GetName())

Проверка через curl

curl --fail-with-body \
  -X POST http://localhost:8080/algoboard.v1.BoardService/GetAgentManifest \
  -H "Content-Type: application/json" \
  -H "Connect-Protocol-Version: 1" \
  -d '{}'

Checklist интеграции

  1. Проверьте GET /readyz.
  2. Получите session cookie через Login или создайте Bearer-токен.
  3. Вызовите GetAppState и сохраните только актуальные UUID.
  4. Отправляйте JSON в lowerCamelCase.
  5. Для частичных обновлений передавайте только изменяемые optional-поля.
  6. Для repeated-полей используйте соответствующий replace... flag.
  7. После MoveIssue перечитайте карточку: могли сработать автоматизации.
  8. После сетевого сбоя сначала проверьте состояние, затем решайте, повторять ли Create.
  9. Перед destructive-операцией покажите пользователю объект и последствия.
  10. Для Jira/YouGile сначала выполните RunImport с dryRun = true.