Документация AlgoBoard
| Параметр | Значение |
|---|---|
| Контракт | algoboard.v1 |
| Обновлено | 9 августа 2026 года |
| Транспорт | ConnectRPC unary, protobuf JSON |
| Локальный Base URL | http://localhost:8080 |
| Источник | backend/proto/algoboard/v1/*.proto |
Руководства по продукту
Практические руководства по объектам, workflow, полям, расчётам, доступу, автоматизациям и остальным разделам настроек опубликованы отдельными страницами в документации AlgoBoard. Этот файл содержит только API и материалы для программного подключения.
Быстрый запрос
- Откройте «Настройки → API и агенты».
- Создайте Agent, назначьте роли и capability policy.
- Скопируйте секрет первого токена: повторно получить его нельзя.
- Передавайте токен в заголовке
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 |
Права агента проверяются в пять слоёв:
- scope агентского токена ограничивает технический класс вызовов;
- capability policy agent principal задаёт для
read,write,adminрешениеallow,askилиdeny; - настраиваемые роли проекта дают атомарные разрешения вроде
issue.view,issue.move,templates.manageилиintegrations.manage; - разрешения полей ограничивают просмотр и изменение системных и созданных данных карточки;
- ACL конкретного представления дополнительно ограничивает операции с этой доской.
Роли назначаются группам, участник получает объединение разрешений всех своих групп. Каталог доступных разрешений приходит в projectState.permissionCatalog, итог текущего участника — в projectState.currentPermissions. Пока в проекте нет ни одной настраиваемой роли, действует совместимый режим Owner/Editor/Viewer. После создания первой роли права не подставляются автоматически: их нужно явно назначить группам. Владелец рабочего пространства получает полный стартовый доступ, только пока ему не назначена ни одна настраиваемая роль; после первого назначения действует обычная редактируемая сумма ролей. Право назначать и изменять владельцев вынесено отдельно в workspace.owners.manage.
Агент использует отдельного служебного участника с principalType=agent; его действия не приписываются владельцу. Решение ask возвращает failed_precondition с одноразовым approval_id до вызова доменного handler, а deny — permission_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 |
Установить приоритет | — | 1–4 |
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-действия 20–35 создают ветки и 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 дополнительно scopeadmin - Контракт:
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 дополнительно scopeadmin - Контракт:
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 дополнительно scopeadmin - Контракт:
DeletePersonnelFieldRequest → DeletePersonnelFieldResponse - Request:
{fieldId:string} - Response:
{}
Значения удаляются каскадно. Если CalculationFormulaReference использует поле персонала, сервер возвращает conflict и не меняет данные.
UpdateMemberProjectProfile
- Доступ:
personnel.manage; для agent token дополнительно scopeadmin - Контракт:
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 дополнительно scopeadmin - Контракт:
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для содержимого, ACLmanageдля структуры и доступа - Контракт:
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 дополнительно scopeadmin - Контракт:
CreateAccessGroupRequest → CreateAccessGroupResponse - Request:
{projectId:string, name:string, description:string, color:string, memberIds:string[], roleIds:string[]} - Response:
{group:AccessGroup}
Создаёт группу только из участников того же workspace и ролей того же проекта. Пустые состав и список ролей допустимы; сервер ничего не подставляет автоматически. Итоговые права участника — объединение разрешений всех ролей всех его групп.
UpdateAccessGroup
- Доступ:
access.manage; для agent token дополнительно scopeadmin - Контракт:
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 дополнительно scopeadmin - Контракт:
DeleteAccessGroupRequest → DeleteAccessGroupResponse - Request:
{groupId:string} - Response:
{}
Удаление блокируется, пока хотя бы одно представление или шаблон формы ссылается на группу. Правила доступа не переписываются скрыто.
CreateProjectRole
- Доступ:
access.manage; для agent token дополнительно scopeadmin - Контракт:
CreateProjectRoleRequest → CreateProjectRoleResponse - Request:
{projectId:string, name:string, description:string, color:string, permissions:string[]} - Response:
{role:ProjectRole}
Роль создаётся без скрытых разрешений. Каждый ключ проверяется по серверному каталогу PermissionDefinition.
UpdateProjectRole
- Доступ:
access.manage; для agent token дополнительно scopeadmin - Контракт:
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 дополнительно scopeadmin - Контракт:
DeleteProjectRoleRequest → DeleteProjectRoleResponse - Request:
{roleId:string} - Response:
{}
Удаление блокируется, пока роль прямо назначена хотя бы одному участнику либо назначена группе.
SetMemberProjectRoles
- Доступ:
access.manage; для agent token дополнительно scopeadmin - Контракт:
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для личного, ACLeditдля общего - Контракт:
CreateBoardFilterCardRequest → CreateBoardFilterCardResponse - Request:
{boardId:string, name:string, filter:BoardFilter, color:string, personal:boolean} - Response:
{card:BoardFilterCard}
UpdateBoardFilterCard
- Доступ: владелец личного фильтра с ACL
viewлибо ACLeditдля общего - Контракт:
UpdateBoardFilterCardRequest → UpdateBoardFilterCardResponse - Request:
{cardId:string, name?:string, filter?:BoardFilter, position?:int32, color?:string} - Response:
{card:BoardFilterCard}
DeleteBoardFilterCard
- Доступ: владелец личного фильтра с ACL
viewлибо ACLeditдля общего - Контракт:
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 дополнительно scopeadmin - Контракт:
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 дополнительно scopeadmin - Контракт:
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 дополнительно scopeadmin - Контракт:
DeleteIssueTemplateRequest → DeleteIssueTemplateResponse - Request:
{templateId:string} - Response:
{}
Ссылка перестаёт работать, но IssueTemplateSubmission остаётся в журнале проекта.
RotateIssueTemplateLink
- Доступ:
templates.manage; для agent token дополнительно scopeadmin - Контракт:
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, executionMode — atomic или 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превращается в JSONlowerCamelCase. 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
}
ExternalLink
{
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
}
subjectType — member или group; access — none, 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[]}
date — YYYY-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 отрицателен после нарушения. status — on_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; decision — allow, 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 интеграции
- Проверьте
GET /readyz. - Получите session cookie через
Loginили создайте Bearer-токен. - Вызовите
GetAppStateи сохраните только актуальные UUID. - Отправляйте JSON в
lowerCamelCase. - Для частичных обновлений передавайте только изменяемые optional-поля.
- Для repeated-полей используйте соответствующий
replace...flag. - После
MoveIssueперечитайте карточку: могли сработать автоматизации. - После сетевого сбоя сначала проверьте состояние, затем решайте, повторять ли Create.
- Перед destructive-операцией покажите пользователю объект и последствия.
- Для Jira/YouGile сначала выполните
RunImportсdryRun = true.