Перейти к основному содержимому

API MBC Dash

Всё, что делает продукт, доступное тому, кто не является браузером: скриптам, CI, вашим собственным инструментам, агенту, которого вы написали сами.

Это руководство. Рассуждения о том, почему оно устроено именно так — почему поверхностей две, почему ключ несёт скоупы, почему идентификаторы работают как работают, — в описании решений.


Где это​

https://dash.mbc-apps.com/api/v1

Всё ниже — относительно этого адреса. /api — само приложение; /v1 — та часть, которая обещает не меняться у вас под руками.

Есть вторая поверхность, /api/… без версии, которой пользуется веб-приложение. Она не для вас: она меняется тогда, когда это нужно интерфейсу, и без предупреждения. Если вы ловите себя на обращении к ней, потому что чего-то не хватает в /api/v1, — скажите об этом: недостающее и есть ошибка.

Как получить ключ​

Настройки → Ключи API → Выпустить ключ. Вы выбираете три вещи:

  • Что можно делать — скоупы, перечислены ниже. Берите наименьший набор, который работает.
  • Куда можно ходить — проекты. Оставите пусто — ключ дотянется всюду, куда дотягиваетесь вы; выберете несколько — на них и остановится, даже там, куда вас бы пустили. Интеграция, которая только заводит баги в один проект, и доставать должна только до одного проекта.
  • Насколько долго — 30, 90, 180 или 365 дней. «Навсегда» нет, и это намеренно.

Секрет показывается один раз. Хранится только его хэш, поэтому восстановить нельзя: потеряли — выпустите другой, а первый отзовите.

curl https://dash.mbc-apps.com/api/v1/workspaces \
-H "Authorization: Bearer mbc_a1b2c3d4-…_xK9…"

X-API-Key: mbc_… тоже работает — для клиентов, которые знают только это написание.

Ключ действует от вашего имени. Он никогда не может больше вас, а скоупы только сужают это дальше. Изменение или отзыв ключа вступают в силу на следующем вызове — никакого токена, который сначала должен истечь.

Имена, а не идентификаторы​

Везде, где путь или поле принимает идентификатор, годится любая из двух форм:

ЧтоПишется какИли как
ЗадачаPROD-12её uuid
ПроектPRODего uuid
СтатусВ работе (его имя внутри проекта)его uuid
Своё полеclient (его ключ) или Клиент (его подпись)его uuid
Рабочее пространствоего имяего uuid
Человекего uuid—

То есть вот это работает:

curl -X PATCH https://dash.mbc-apps.com/api/v1/tasks/PROD-12 \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"status":"В работе","priority":"HIGH"}'

Имена сопоставляются без учёта регистра и всегда внутри того, чему принадлежат: имя статуса ничего не значит за пределами своего проекта. Если имя подходит более чем одному объекту, вы получите ошибку со списком кандидатов, а не молчаливый выбор первого.

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

Каждый объект несёт также url в веб-приложение, чтобы скрипт мог сообщить о чём-то, на что человеку можно кликнуть.

Что можно вызывать​

Двадцать две операции. Каждая называет нужный ей скоуп.

Задачи​

Скоуп
GET/tasks/{task}tasks:read
POST/tasks/searchtasks:read
GET/tasks/{task}/subtaskstasks:read
POST/spaces/{space}/taskstasks:write
PATCH/tasks/{task}tasks:write
POST/tasks/{task}/assigneestasks:write
DELETE/tasks/{task}/assignees/{user}tasks:write
POST/tasks/{task}/tagstasks:write
DELETE/tasks/{task}/tags/{tag}tasks:write
PUT/tasks/{task}/fields/{field}tasks:write
DELETE/tasks/{task}tasks:delete

Удаление обратимо тридцать дней: задача и её подзадачи уходят в корзину вместе и возвращаются вместе. Именно поэтому tasks:delete — отдельный скоуп: «может править задачи» и «может их убирать» — разные решения.

Комментарии​

Скоуп
GET/tasks/{task}/commentscomments:read
POST/tasks/{task}/commentscomments:write

Тексты — Markdown.

Структура​

Скоуп
GET/workspacesstructure:read
GET/workspaces/{workspace}/spacesstructure:read
GET/spaces/{space}structure:read
GET/spaces/{space}/listsstructure:read
GET/spaces/{space}/statusesstructure:read
GET/spaces/{space}/fieldsstructure:read
POST/workspaces/{workspace}/spacesstructure:write
POST/spaces/{space}/listsstructure:write
GET/workspaces/{workspace}/membersmembers:read

Проекту нужно только имя; не укажете key — он будет выведен из имени. Задаче нужен только проект: список подберётся сам, а если у проекта его нет — создастся.

Полный перечень, от самого API​

Документ OpenAPI описывает каждую операцию, её аргументы и её ответы:

GET /api/v3/api-docs

Swagger UI — по адресу /api/swagger-ui.html. Операции /v1 помечены тегом Public API.

Поиск задач​

POST /tasks/search говорит на том же языке фильтров, что и подборки в интерфейсе, и из этого следуют две полезные вещи: вы не можете выразить поиск, которого продукт не покажет, а всё, что выразили, можно сохранить как подборку и открыть человеком.

curl -X POST https://dash.mbc-apps.com/api/v1/tasks/search \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{
"conditions": [
{"field": "space", "op": "in", "values": ["PROD"]},
{"field": "status", "op": "notIn", "values": ["Готово"]},
{"field": "assignee", "op": "in", "values": ["me"]},
{"field": "due", "op": "overdue"}
],
"sort": [{"field": "due", "dir": "asc"}],
"limit": 50
}'

Условия соединяются по И. Значения внутри одного условия — по ИЛИ.

Поля: workspace, space, status, statusCategory, assignee, creator, priority, type, tag, due, updated, created, customField, title, text.

Операторы: in, notIn, empty, before, after, between, overdue, today, thisWeek, contains.

Не всякий оператор подходит всякому полю, и запрос неподходящего — ошибка, которая об этом говорит. Даты идут в from / to как ISO-моменты, а не в values.

Две вещи, которые стоит знать:

updated — это то, как вы спрашиваете, что изменилось. Без него интеграции приходится перечитывать всё на каждом запуске:

{"field": "updated", "op": "after", "from": "2026-08-01T00:00:00Z"}

customField требует, чтобы поиск сводился к одному проекту, и называет поле в key:

{"field": "customField", "op": "in", "key": "Клиент", "values": ["Acme"]}

Описание поля принадлежит проекту, поэтому одно и то же имя может означать число в одном и дату в другом. Если ваш поиск достаёт до нескольких проектов, вы получите ошибку с объяснением, а не догадку. Операторы здесь — in, notIn и empty.

me означает владельца ключа для assignee и creator. today и thisWeek вычисляются на каждый запрос в часовом поясе вызывающего — передайте его как "timezone": "Europe/Moscow", если это важно.

Скоупы​

СкоупЧто позволяет ключу
tasks:readчитать задачи, их подзадачи, значения их своих полей
tasks:writeсоздавать и изменять задачи, исполнителей, метки, значения полей
tasks:deleteудалять задачи (обратимо тридцать дней)
comments:read / comments:writeчитать / писать комментарии
attachments:read / attachments:writeчитать / добавлять вложения — зарезервировано, операций пока нет
structure:readвидеть рабочие пространства, проекты, списки, статусы, описания полей
structure:writeсоздавать и изменять их
structure:deleteудалять или архивировать их — зарезервировано, ни одна операция пока не использует
views:read / views:writeчитать / писать подборки — зарезервировано, операций пока нет
members:readвидеть, кто состоит в рабочем пространстве — нужно, прежде чем кого-то назначать
audit:readчитать журнал аудита

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

Когда что-то идёт не так​

Ошибки — документы RFC 7807 с устойчивым машиночитаемым code:

{
"type": "https://errors.mbc.studio/validation_error",
"title": "Unprocessable Entity",
"status": 422,
"detail": "В этом проекте нет статуса «В работах»; доступны: [Открыта, В работе, Готово]",
"code": "validation_error",
"instance": "/api/v1/tasks/PROD-12",
"timestamp": "2026-08-18T10:15:00Z"
}

Ветвитесь по code, показывайте detail — он написан, чтобы его читали, и обычно говорит, что отправить вместо этого.

СтатусЗначит
401ключа нет, он истёк или отозван
403ключу не хватает скоупа; detail называет какого
404этого не существует — или ваш ключ огорожен от него, что намеренно отвечает так же
409конфликт, например ключ проекта уже занят
422запрос понят и отклонён; detail говорит почему
429слишком быстро — см. лимиты

Каждый ответ несёт X-Request-Id. Приводите его, если нужно, чтобы кто-то что-то посмотрел.

Что изменится, а что нет​

/api/v1 только дополняется. Новые эндпойнты, новые необязательные поля запроса и новые поля ответа могут появиться в любой момент — поэтому игнорируйте незнакомые поля, а не падайте на них. Ничто не удаляется, не переименовывается, не сужается и не становится обязательным. Ломающее изменение стало бы /api/v2 рядом с этим, с объявленным периодом сосуществования.

Это проверяется механически: сгенерированное описание /api/v1 лежит в репозитории, и сборка падает на любом изменении, которое обещание запрещает.

Отказ от поддержки, если он когда-нибудь случится, придёт заголовками Deprecated и Sunset и пометкой в документе OpenAPI, а не письмом.

Как безопасно повторять​

Таймаут не говорит вам ничего о том, произошла работа или нет. Отправляйте на записи Idempotency-Key, и повтор получит первый ответ вместо того, чтобы сделать всё заново:

curl -X POST https://dash.mbc-apps.com/api/v1/spaces/PROD/tasks \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-H 'Idempotency-Key: nightly-sync-2026-08-18-item-7' \
-d '{"title":"Ровно одна"}'

Повтор отвечает тем же статусом и тем же телом, плюс Idempotent-Replay: true, чтобы вы могли отличить. Записи хранятся сутки.

Один ключ на один отдельный вызов. Повторное использование ключа с другим телом отклоняется с 409, а не отвечается прежним результатом: почти всегда это ключ, вынесенный из цикла, и ответить на него значило бы, что вторая сущность молча никогда не появилась. Два одинаковых запроса, пришедших одновременно, тоже получают 409 на проигравшем, с сообщением, что первый ещё выполняется; повторите его.

Ключи ваши, а не общие: другая интеграция может использовать ту же строку и иметь в виду другое.

Лимиты​

На ключ, в минуту, тремя ярусами:

По умолчанию
Чтения600/мин
Записи120/мин
Удаления20/мин

Сверх лимита — 429; притормозите, а не повторяйте немедленно.

Удаления намеренно самые тугие, и массового удаления в API нет — поэтому «удалить всё выполненное» стоит одного вызова на задачу и упирается в этот лимит задолго до конца списка. Это задуманное поведение, а не препятствие, которое надо обойти.

Человека в браузере эти лимиты не касаются; они для машин.

Как быть хорошим соседом​

  • Спрашивайте, что изменилось, а не всё подряд: updated after <прошлый запуск> — это одна страница вместо всех.
  • Храните uuid, если не готовы сломаться от переименования; пишите имена, если предпочитаете, чтобы вызовы читались.
  • Сужайте скоупы и огораживайте ключи проектами. Радиус поражения утёкшего ключа — ровно то, что вы ему дали.

Чего здесь пока нет​

Записано, чтобы вы не искали:

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

Почему каждое отложено — в описании решений.