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

Спецификация API

Полный справочник по REST-эндпоинтам. Туториалы и примеры см. в Руководстве по API.

Всё, что участник проекта может делать в веб-интерфейсе, доступно здесь — SPA потребляет тот же самый API. Операции, требующие роли manager, помечены (manager); всему остальному нужно лишь членство в проекте (или, для операций чтения, помеченных (viewer), любой уровень доступа). В таблицах ниже перечислена каждая группа маршрутов, которую монтирует сервер; те, что сведены к одной строке, полностью описаны в актуальном openapi.json.

https://eastagiletracker.com/api/v1

https://api.eastagiletracker.com/api/v1 обслуживает идентичный API. Все запросы и ответы — это JSON, за исключением нескольких эндпоинтов загрузки файлов, которые принимают multipart.

Две группы находятся уровнем выше, под /api, а не /api/v1: поверхность аутентификации (/api/auth/*) и публичные формы (/api/contact, /api/feedback). Их написание через /api/v1/… возвращает 404.

Каждый аутентифицированный запрос отправляет учётные данные одним из способов:

  • X-TrackerToken: <key>
  • Authorization: Bearer <key>

Пользовательские ключи начинаются с ea_user_, ключи агентов — с ea_agent_, а access-токены MCP — с ea_mcp_. См. Руководство по API → Три вида учётных данных.

Эндпоинты без аутентификации: /openapi.json, /docs, эндпоинты /api/auth/* и справочные данные (/story_types, /story_states, /effort_scales, /priority_scales). /meta требует аутентификации — подходит любой действительный ключ, но он не ограничен проектом (привязанный к проекту ключ агента тоже до него дотягивается).

Четыре уровня управляют эндпоинтами, ограниченными проектом:

LevelWho passesTypical operations
public viewerкто угодно, в проекте с публичной видимостьючтение доски: истории, итерации, поиск, активность по историям и эпикам (с сокрытыми данными актора)
viewerviewer, member, managerчтение (список/получение историй, поиск, метрики, список форматов экспорта)
membermember, managerвсе операции записи рабочих элементов (истории, задачи, комментарии, …), поток событий
managerтолько managerнастройки проекта, управление членством, ключи агентов, удаление, импорт, скачивание экспорта, резервные копии, журнал аудита

Агенты держат те же роли, что и участники, — viewer, member или manager, — с потолком в виде роли выпустившего ключ участника. Не-участник получает 404 unfound_resource (а не 403) на путях приватного проекта, так что ID проектов не перечислимы.

MethodPathDescription
GET/openapi.jsonАктуальная спецификация OpenAPI 3, включая тела запросов. Без аутентификации.
GET/docsSwagger UI. Без аутентификации.
GET/metaИдентичность вызывающего (auth.kind/key_id/agent_id/project_id) + граф переходов для типов историй. Требует аутентификации (любой действительный ключ; не ограничен проектом). Вызывайте это первым.
GET/api/health · /api/configПроверка живости и публичная конфигурация развёртывания (режим одной организации, включённые необязательные функции, название экземпляра). Без аутентификации, вне /v1.

Эндпоинты сессий, без аутентификации, если не указано иное. Ими управляет SPA; скрипты обычно используют вместо этого API-ключ.

MethodPathDescription
POST/auth/registerЗарегистрировать новый аккаунт — защищено reCAPTCHA; затем аккаунт проходит SMS-проверку
POST/auth/sms/challenge · /auth/sms/status · /auth/sms/bypassОтправить / проверить код регистрации по SMS (bypass открыт только оператору)
GET/auth/configКакие способы входа предлагает развёртывание
POST/auth/loginВход по email + паролю; возвращает JWT сессии либо TOTP-запрос
POST/auth/login/totpЗавершить вход кодом из приложения-аутентификатора или кодом восстановления
POST/auth/passkey/login/start · /auth/passkey/login/finishБеспарольный вход через WebAuthn
GET/auth/oauth/github/start · /auth/oauth/github/callback · /auth/oauth/google/start · /auth/oauth/google/callback · POST /auth/oauth/exchangeВход через OAuth с GitHub или Google
POST/auth/refresh · /auth/refresh/revokeРотировать refresh-токен / отозвать его
POST/auth/logoutВыйти (отзывает refresh-токен)
POST/auth/forgot-password · /auth/reset-passwordЗапросить письмо для сброса / использовать токен сброса
POST/auth/accept-invite/lookup · /auth/accept-inviteРазрешить токен приглашения → email / принять приглашение в проект (после аутентификации)

Эти действуют от имени вызывающего и требуют лишь действительного ключа (без роли в проекте).

MethodPathDescription
GET/meПрофиль текущего пользователя
PUT/meОбновить профиль
DELETE/meУдалить аккаунт — отклоняется, пока вы единственный владелец организации или проекта, в котором есть другие участники
GET/me/deletion-impactЧто удаление аккаунта уберёт и что ему мешает
PUT/me/passwordСменить пароль
PUT/me/settingsОбновить настройки (тема, предпочтения уведомлений)
POST/me/avatarЗагрузить аватар (multipart)
POST/me/api-token/regenerateРотировать ваш API-токен — аннулирует существующие сессии/ключи
GET/me/api_keys · POST /me/api_keys · DELETE /me/api_keys/{id}Управление пользовательскими (ea_user_) API-ключами
GET/me/totp · POST /me/totp/setup · /me/totp/verify · /me/totp/disableПодключение двухфакторной аутентификации (TOTP); verify один раз возвращает коды восстановления
GET/me/passkeys · POST /me/passkeys/register/start · /me/passkeys/register/finish · DELETE /me/passkeys/{credential_id}Регистрация и удаление passkey
GET/me/oauth_grants · DELETE /me/oauth_grants/{grant_id}Подключённые приложения — MCP-клиенты и OAuth-приложения, которые вы авторизовали
GET/me/activityВаша активность по всем проектам
GET/me/storiesИстории, которыми вы владеете, которые запросили или за которыми следите, во всех проектах, доступных токену — role=owned|requested|following, state=, cursor= / limit= (максимум 200)
GET/me/mentions · POST /me/mentions/{mention_id}/ackВходящие @-упоминания (unacked=true для фильтрации) и подтверждение — также сведены в ленту уведомлений ниже
GET/me/data-exportGDPR-самоэкспорт ваших данных
GET/me/consent · POST /me/consentПрочитать / записать согласие ({ consent_type, granted })
GET/legal/pending · POST /legal/acceptОжидающие clickwrap-документы / запись принятия
GET / PUT/agent/meСобственная идентичность и профиль ключа агента, доступные агенту на чтение и запись (аналог /me со стороны агента)
POST/api/contact · /api/feedback · /api/feedback/with-screenshotКонтакт + обратная связь внутри приложения. Вне /v1; с ограничением частоты по IP

Стартовые справочники, используемые при создании/оценке историй. Стабильные ID.

MethodPathDescription
GET/story_typesfeature, bug, chore, release (+ allow_points)
GET/story_statesunstarted … accepted, rejected
GET/effort_scalesдоступные шкалы оценки
GET/effort_scales/{scale_id}/valuesзначения баллов в шкале
GET/priority_scales · /priority_scales/{scale_id}/valuesшкалы приоритетов и их значения (priority_id истории разрешается здесь)

Только в хостируемом сервисе — self-hosted-установка работает в режиме одной организации и не монтирует эти эндпоинты (кроме списка организаций). Роли здесь организационные: owner, admin, member.

MethodPathDescription
GET / POST/organizationsСписок ваших организаций / создать одну
GET / PUT / DELETE/organizations/{oid}Прочитать, переименовать (имя + slug; owner или admin), удалить
GET / POST/organizations/{oid}/memberships · PUT / DELETE …/memberships/{member_id}Участники и приглашения; у приглашения есть потолок роли (никогда выше роли самого приглашающего; owner никогда не выдаётся приглашением)
POST/organizations/{oid}/memberships/bulk-role · …/memberships/bulk-removeСмена роли или удаление до 200 участников за раз. Всё или ничего: пакет, который удалил бы последнего owner или оставил проект без владельца, отклоняется целиком; с reassign_confirmed владельцем этих проектов становитесь вы
DELETE/organizations/{oid}/invitations/{invitation_id}Отозвать ожидающее приглашение
POST/organizations/{oid}/transfer-ownershipПередать роль owner другому участнику
PUT/organizations/{oid}/memberships/{member_id}/anonymizationСкрыть имя / email / аватар участника во всей организации
GET/organization-invitations/{token} · POST …/{token}/acceptРазрешить / принять присланное по почте приглашение в организацию
POST/organizations/{oid}/export · GET / POST …/export/jobs · GET …/export/jobs/{job_id} · …/export/jobs/{job_id}/downloadЭкспорт организации, только для владельца: zip с SQL-дампом и каждым вложением, выполняется как задача
MethodPathDescription
GET/projectsСписок ваших проектов (limit ≤ 200)
POST/projectsСоздать проект
GET/projects/{id}Получить детали проекта (viewer)
PUT/projects/{id}Обновить настройки проекта (manager)
DELETE/projects/{id}Удалить проект (manager)
POST/projects/{id}/pinЗакрепить / открепить проект в вашем списке проектов
POST/projects/{id}/transfer-organizationПеренести проект в другую организацию (manager)
POST/projects/{id}/slack/testОтправить тестовое сообщение в Slack-ленту проекта (manager)
GET / POST/projects/{id}/showcase_claim_eligibility · /projects/{id}/showcase_claim · /projects/{id}/showcase_seedПубличные витринные проекты: проверить, можете ли вы забрать такой проект, забрать его, наполнить его данными
GET/projects/{id}/audit-logЧтение журнала аудита — история проекта плюс активность по историям / по эпикам через surface=; доступ зависит от surface, см. ниже
GET/projects/{id}/eventsПоток событий с курсорной пагинацией (member) — см. Events

Query-параметры журнала аудита: event_type= (один тип или список через запятую), limit= (≤ 1000), before= (keyset-курсор, created_at в ISO-8601), surface= (project_history, story_activities, epic_activities), target_id= (id истории/эпика — обязателен, когда surface=story_activities или epic_activities). Доступ: нефильтрованный лог и surface=project_history(manager); story_activities / epic_activities может читать любой участник проекта, а в публичных проектах — и анонимно, со скрытыми персональными данными актора.

MethodPathDescription
GET/projects/{id}/membershipsСписок участников (viewer)
POST/projects/{id}/membershipsПригласить участника по email (manager)
PUT/projects/{id}/memberships/{mid}Обновить роль (manager)
DELETE/projects/{id}/memberships/{mid}Удалить участника (manager)
GET/projects/{id}/addable-members · POST /projects/{id}/members/add-existingУчастники организации, ещё не добавленные в проект / добавить одного без приглашения по почте (manager)
POST/projects/{id}/members/joinOwner или admin организации присоединяется к проекту своей организации как manager либо повышает себя до этой роли (действие Make me owner в списке проектов)
PUT/projects/{id}/members/{mid}/anonymizationСкрыть имя / email / аватар участника в этом проекте (manager)
GET / POST/projects/{id}/agent_keysСписок / выпуск ключей агентов — менеджеры либо те роли, которые допускает политика ролей-создателей проекта
DELETE/projects/{id}/agent_keys/{kid}Отозвать ключ агента
GET/projects/{id}/agent_keys/onboardingСтартовый набор: промпты и конфигурационные файлы для распространённых агентских клиентов
GET/projects/{id}/agents · GET / PUT /projects/{id}/agents/{aid}Агенты проекта и их профили (имя, инициалы, описание, цвет)
POST/projects/{id}/agents/{aid}/rotate-key · /projects/{id}/agents/{aid}/avatarРотировать ключ агента (идентичность и история сохраняются) / загрузить его аватар

Всем операциям записи историй нужна роль member.

MethodPathDescription
GET/projects/{id}/storiesСписок историй (с пагинацией, фильтруемый) (viewer)
POST/projects/{id}/storiesСоздать историю
GET/projects/{id}/stories/{sid}Получить одну историю (viewer)
PUT/projects/{id}/stories/{sid}Обновить историю
DELETE/projects/{id}/stories/{sid}Удалить историю
POST/projects/{id}/stories/{sid}/transitionsСменить состояние с валидацией
POST/projects/{id}/stories/{sid}/reject · /projects/{id}/stories/{sid}/restartОтклонить доставленную историю / вернуть отклонённую в started (для /transitions состояние rejected терминально)
POST/projects/{id}/stories/{sid}/archive · /projects/{id}/stories/{sid}/unarchiveАрхивировать / разархивировать одну историю
POST/projects/{id}/stories/bulk_transitionПеревести много историй (1–100) сразу
POST/projects/{id}/stories/bulk-archive · bulk-delete · bulk-duplicate · bulk-moveАрхивировать, удалить, дублировать или переместить (в панель / на позицию) много историй
POST/projects/{id}/stories/{sid}/duplicateДублировать одну историю
GET / POST / DELETE/projects/{id}/stories/{sid}/epics · …/epics/{eid}Принадлежность истории эпикам
GET/short-links/{code} · /story-referencesРазрешить короткую ссылку /s/<code> в её историю / разрешить до 100 ссылок на истории (#id, URL) в истории, доступные вызывающему

Параметры запроса списка историй: archived= (exclude по умолчанию / include / only — трёхзначный фильтр архивных историй; заменяет устаревший include_archived=true, который теперь является псевдонимом archived=include), include_done=true (допускает истории панели Done, замороженные на прошлых итерациях; по умолчанию исключены). Пагинация (cursor= / limit= / offset=) и разреженные наборы полей (fields=) следуют разделам Пагинация и Проекция полей.

Create (POST …/stories): { "name" (required), "story_type": "feature|bug|chore|release", "description"?, "estimate"?, "current_state"?, "icebox"?, "labels"? }. estimate — это подпись значения шкалы в виде строки ("3", "13"); JSON-число отклоняется. labels принимает ["auth"] или [{ "name": "auth" }]; неизвестные метки создаются. Значения по умолчанию: story_type=feature, current_state=unstarted.

Update (PUT …/stories/{sid}): те же поля, все опциональные, плюс "position" (float), "force_state_change" (bool) и "expected_updated_at" (RFC 3339 — сохранение описания отклоняется с 409 stale_write, если история изменилась с момента вашего чтения). Операции записи истории также учитывают If-Match относительно ETag истории; несовпадение — это 412 precondition_failed.

Transition (POST …/transitions): { "to": "<state>" }. Поле называется to. Возвращает { story_id, state }. Недопустимое перемещение → 422 invalid_transition с details: { from, to, allowed }.

Bulk transition (POST …/bulk_transition): { "story_ids": [int,…] (1–100), "to": "<state>" }. Каждая история оценивается независимо; возвращает { results: [ { id, status: "ok" } | { id, status: "failed", error } ] }.

Все member. List/GET для большинства — (viewer).

MethodPathBody / notes
GET / POST/projects/{id}/stories/{sid}/tasks · PUT/DELETE …/tasks/{tid}{ description (or task_desc), complete?, task_order? }
GET / POST/projects/{id}/stories/{sid}/comments · PUT/DELETE …/comments/{cid}{ text (or comment_text) } или { comment_emoji }. GET принимает fields= (список разрешённых: comment_id, story_comment_id, story_id, comment_text, comment_emoji, member, created) плюс cursor= / limit= (≤ 200) / order=asc|desc
GET / POST/projects/{id}/stories/{sid}/blockers · PUT/DELETE …/blockers/{bid}{ blocker_desc, resolved? }
GET / POST/projects/{id}/stories/{sid}/links · PUT/DELETE …/links/{lid}{ url, link_type?, title? }link_typerelates_to, duplicates, blocks, is_blocked_by, pull_request, branch, other; URL GitHub вида /pull/ и /tree/ типизируются автоматически
GET / POST/projects/{id}/stories/{sid}/reviews · PUT/DELETE …/reviews/{rid}Создание: { reviewer_id? / reviewer_agent_id?, comment? } — опустите оба, чтобы назначить себя. Обновление: { status, comment? }
GET / POST/projects/{id}/stories/{sid}/owners · DELETE …/owners/{mid} · DELETE …/owners/agents/{aid}{ member_id? / agent_id? } — опустите оба, чтобы добавить вызывающего
GET / POST/projects/{id}/stories/{sid}/followers · DELETE …/followers/{mid} · DELETE …/followers/agents/{aid}{ member_id? / agent_id? }
GET / POST/projects/{id}/stories/{sid}/labels · DELETE …/labels/{lid}{ name }
GET / POST/projects/{id}/stories/{sid}/attachments (+ /json) · DELETE …/attachments/{aid}multipart-загрузка — видео ≤ 200 MB, PDF / Word / Excel ≤ 25 MB, изображения / CSV / текст ≤ 10 MB; список — (viewer)
GET / POST/projects/{id}/stories/{sid}/link-attachments · DELETE …/link-attachments/{laid}Вложения-ссылки — внешний URL, который хранится рядом с файловыми вложениями, а не как ссылка на код
GET/attachments/{token} · /api/avatars/{token}Чтение вложения или аватара по токену — именно такие URL выдаёт API; X-TrackerToken не нужен

Та же форма, что и у историй, только без конечного автомата состояний. member для записи, (viewer) для чтения.

MethodPathDescription
GET / POST/projects/{id}/epics · GET / PUT / DELETE …/epics/{eid}У эпика есть имя, Markdown-описание и стоящая за ним метка, которая объединяет его истории
GET / POST / PUT / DELETE…/epics/{eid}/comments · …/comments/{cid}Комментарии к эпику
GET / POST / DELETE…/epics/{eid}/owners · …/followers (+ варианты /agents/{aid})Владельцы и подписчики, участники или агенты — владельцы эпика распространяются на его истории
GET / POST / DELETE…/epics/{eid}/attachments (+ /json) · …/link-attachmentsВложения, ограничения те же, что у историй
GET/projects/{id}/analytics/epics · …/analytics/epics/{eid}Прогресс по каждому эпику: burnup, пропускная способность, здоровье, прогноз (viewer)

member для записи, (viewer) для чтения.

MethodPathDescription
GET / POST/projects/{id}/labelsСписок / создание метки
PUT / DELETE/projects/{id}/labels/{lid}Обновление / удаление метки
POST/projects/{id}/labels/{lid}/archiveАрхивировать (мягко скрыть) метку

Чтение открыто для любой роли в проекте, а в публичном проекте — и анонимно.

MethodPathDescription
GET/projects/{id}/iterationsСписок итераций (≤ 500 на страницу; несёт ETag, а при усечении — заголовки продолжения X-Tracker-Pagination-*)
GET/projects/{id}/iterations/{itid}Одна итерация
GET/projects/{id}/iterations/first-previewДаты, которые получит первая итерация, — показываются в подтверждении при её создании
POST/projects/{id}/iterationsСоздать ручную итерацию (member)
DELETE/projects/{id}/iterations/{itid}Удалить итерацию (manager)
PUT/projects/{id}/iterations/{itid}/velocityПереопределить velocity одной итерации, не меняя стратегию проекта (manager)
GET/projects/{id}/iterations/{itid}/done-storiesПринятые истории закрытой итерации, с пагинацией
MethodPathDescription
GET/projects/{id}/search?q=…Мощный поиск — полнотекстовый + фасетные, датовые и «людские» квалификаторы (DSL в стиле GitHub); возвращает { results, total, limit, offset }. query — псевдоним q; limit= (по умолчанию 50, максимум 1000) / offset= — пагинация; sort= сортирует по relevance (по умолчанию), created, created_asc, state или updated. (viewer) — см. Руководство
GET/projects/{id}/metrics/{velocity,burndown,story-types,contributors}Ряды данных страницы Metrics (viewer); метрики по эпикам находятся в /analytics/epics выше
GET/projects/{id}/backlog/groupingСпроецированные группы итераций из Backlog (viewer)
GET / PUT/projects/{id}/preferencesВаши предпочтения доски для этого проекта — любая роль в проекте, только ваша собственная строка
MethodPathDescription
GET/projects/{id}/eventsПоток событий с курсорной пагинацией (member) — viewer получает 403

Параметры запроса: since=<event_id>, types=story.created,story.transitioned,comment.added,…, limit= (≤ 500), cursor=. Ответ включает next_cursor. Передайте последний увиденный event_id как since, чтобы возобновить.

Единая лента уведомлений в приложении: полноценные строки уведомлений (запросы на ревью, активность по story, приглашения, …), объединённые с входящими @-упоминаниями в один поток, сначала новые. Id в ленте имеют префикс источника (nt-… / sc-… / ec-…). Сессии участников и ключи ea_user_* читают свои строки участника; ключи ea_agent_* — свои строки агента.

MethodPathDescription
GET/me/notificationsВаша лента уведомлений. Фильтры: unread=true, since_id=, kind= (mentions / reviews / stories / invitations); пагинация через cursor= / limit=
GET/me/notifications/unread-countСчётчики непрочитанного — { unread_count, by_category: { mentions, reviews, stories, invitations } }
POST/me/notifications/read-allОтметить всё прочитанным; возвращает свежие счётчики
POST/me/notifications/{id}/ackОтметить один элемент прочитанным (идемпотентно)
POST/me/notifications/{id}/acceptПринять приглашение в проект / организацию прямо из ленты (только токены участника)
POST/me/notifications/{id}/declineОтклонить приглашение в проект / организацию (только токены участника)
GET/me/notifications/resolve-invite?token=…Сопоставить присланный по почте токен приглашения с id вашего уведомления — { "id": "nt-…" } или { "id": null }
GET/me/notifications/streamЖивой push — Server-Sent Events (text/event-stream); см. ниже

Stream-эндпоинт — не JSON-эндпоинт, поэтому его нет в спецификации OpenAPI: он держит соединение открытым и при каждом новом событии отправляет кадр без полезной нагрузки ({"type":"notification","kind":…}), сигнализируя клиенту перезагрузить ленту. Соединения закрываются сервером через 45 минут — переподключитесь и пройдите аутентификацию заново. Только сессии участников и ключи ea_user_*; ключи ea_agent_* получают 403.

MethodPathDescription
POST/projects/{id}/importФайловые источники: source=pivotal, jira, asana, gitlab, shortcut, trello, linear, plane, plane_json, eat. Multipart file=. Синхронный — отвечает счётчиками результата.
POST/projects/{id}/import/jsonJSON-тело; source=github не требует файла — owner, repo, опциональный token и включаемые по выбору флаги include_pull_requests / include_milestones / include_releases / include_dependencies; файловые источники присылают file_base64. Асинхронный: возвращает 202 { import_id, status }. Сервер выбирает данные через GraphQL API GitHub, который отклоняет анонимных вызывающих, поэтому до GitHub всегда доходит токен — ваш либо общий токен развёртывания. См. Руководство.
GET/projects/{id}/imports/{import_id}Опросить задачу: status проходит pending → fetching → writing → done | failed, с progress_current / progress_total во время выборки и счётчиками результата при done

В проекте одновременно выполняется один импорт; второй POST, пока один в работе, даёт 409 import_already_running. dry_run: true (в JSON-теле или dry_run=true в multipart) даёт предпросмотр любого источника: разбирает, сопоставляет, устраняет дубликаты, возвращает те же счётчики { imported, skipped, errors, unmatched }, а затем откатывает — ничего не записывается. Ограничения: тело 10 МиБ и 5000 историй на импорт для файловых источников (сверх любого → 400, ничего не записывается). У источника GitHub потолка нет — он пишет порциями, а не одной транзакцией. Повторный импорт идемпотентен по id в источнике — уже импортированные строки пропускаются, а не дублируются.

MethodPathDescription
GET/projects/{id}/export/formatsЗарегистрированные форматы: { id, name, content_type, drops, includes_archived }. Любая роль в проекте.
GET/projects/{id}/export/{format}Скачать один (manager). Обмен: eat (полная точность), jira, pivotal, shortcut, trello, asana, gitlab, linear, plane, plane_json; документы: pdf, docx.
GET/projects/{id}/export/attachmentsВсе вложения одним просматриваемым zip (файлы сохраняют исходные имена; манифест JSON + CSV) (manager).

Экспорты документов (pdf, docx) принимают дополнительные параметры запроса: page_size= (letter по умолчанию / a4 / legal / folio), from= / to= (границы окна историй — RFC 3339 или просто YYYY-MM-DD; история попадает в диапазон, когда её created или completed_at лежит внутри него), include_icebox= / include_backlog= (оба по умолчанию false, поэтому предназначенный для распространения экспорт показывает только запланированную / текущую работу). Форматы обмена CSV их игнорируют.

MethodPathDescription
GET / POST/projects/{id}/backups · GET …/backups/{snapshot_id} · …/backups/healthСписок снимков, создать снимок прямо сейчас, прочитать один, а также сводка по здоровью хранения
GET / POST/projects/{id}/restores · POST …/restores/partial · GET …/restores/{restore_id}Восстановить снимок целиком либо выбранные таблицы из него и опросить ход восстановления

Эти POST находятся в чувствительном уровне ограничения частоты (см. ниже).

East Agile Tracker — это OAuth 2.1-провайдер для MCP-клиентов. Клиент обнаруживает его по /.well-known/oauth-authorization-server и /.well-known/oauth-protected-resource/mcp, отправляет вас на /oauth/authorize (страница согласия), обменивает код на /oauth/token, а затем говорит по MCP на /mcp с полученным токеном ea_mcp_*. Выданные разрешения перечисляются и отзываются на /me/oauth_grants. У эндпоинтов провайдера свой уровень ограничения частоты.

wss://eastagiletracker.com/ws/control?token=<session JWT>

Для интерактивного удалённого управления интерфейсом ({ "action": "get_state", "id": "req-1" }). Токен — это JWT сессии браузера; API-ключ отклоняется с 401 ещё до апгрейда соединения. Не канал данных — все чтения/записи идут через REST. Только для одного инстанса; не распределяется между репликами.

Эндпоинты записи (POST, PUT, DELETE) принимают заголовок Idempotency-Key. Тот же ключ + то же тело воспроизводят кэшированный ответ (окно 24 часа); тот же ключ + другое тело возвращают 409 idempotency_conflict. Область действия ключа ограничена теми учётными данными, которые его отправили. Не применяется к GET/HEAD/OPTIONS, /openapi.json и /docs, /api/auth/* или multipart-загрузкам на путях /attachments. Ответы, не дошедшие до доменного ответа, никогда не кэшируются — 401, 403, 404, 429 и любой 5xx, — поэтому повтор после любого из них доходит до обработчика; 400, 409, 412 и 422 являются ответом домена и воспроизводятся так же, как успех.

Эндпоинты списков принимают cursor=<opaque> и limit=<n>. Когда заданы, ответ — это { "items": [...], "next_cursor": "<str|null>" }; передайте next_cursor обратно для перехода на страницу. Потолок limit задаётся для каждого эндпоинта: 200 у историй, комментариев и проектов; 500 у событий; 1000 у поиска и журнала аудита.

Обычный список (без cursor/limit), которому пришлось усечь ответ, сообщает об этом в заголовках — X-Tracker-Pagination-Truncated, X-Tracker-Pagination-Limit, X-Tracker-Pagination-Offset и X-Tracker-Pagination-Next-Offset; последний передайте обратно как offset= для следующей страницы. Заголовка с общим количеством нет.

Эндпоинты списков принимают fields= (через запятую), чтобы возвращать только конкретные поля. story_id включается всегда; неизвестное имя поля возвращает 400 validation_failed с нарушающими именами в details.fields.

GET /projects/123/stories?fields=story_id,name,current_state,owners

У каждой JSON-ошибки есть code и error; некоторые добавляют details:

{ "code": "invalid_transition",
"error": "Cannot move story from `unstarted` to `accepted`",
"details": { "from": "unstarted", "to": "accepted", "allowed": ["started"] } }
StatuscodeWhen
400invalid_parameterплохой ввод; сообщение в error, без details (большинство валидаций: пусто/длина/null-байт/email)
400validation_failedструктурированная ошибка ввода; details.fields — это массив имён нарушающих полей
401unauthenticatedотсутствующий/недействительный токен
403unauthorized_operationаутентифицирован, но недостаточная роль
404unfound_resourceне найдено — также возвращается не-участникам
409conflictконфликт ресурса (например, дубликат)
409idempotency_conflictIdempotency-Key повторно использован с другим телом
409stale_write · import_already_runningистория изменилась с момента вашего expected_updated_at · импорт уже выполняется
412precondition_failedIf-Match не совпал с текущим ETag ресурса; details несёт expected и current
413request_too_largeтело превышает лимит размера для этого маршрута
422invalid_transitionнедопустимое перемещение состояния; details несёт { from, to, allowed }
429rate_limitedслишком много запросов с этого IP на маршруте с ограничением частоты; заголовок Retry-After
500internal_errorсбой сервера — обобщённое сообщение; безопасно повторять
503not_configuredв развёртывании нет интеграции, которая нужна этому маршруту (SMS, объектное хранилище, …)

details.fields — это JSON-массив имён полей (например, ["to"]), иногда с дополнительными ключами вроде max. Карты «поле→сообщение» нет.

{ "code": "validation_failed", "error": "unknown field(s): foo", "details": { "fields": ["foo"] } }

По IP клиента, на нескольких маршрутах; остальной аутентифицированный API-трафик не ограничивается по частоте. Значения по умолчанию (каждая пара — это установившаяся частота и всплеск, настраивается оператором):

  • Auth/api/auth/*: 0.5 запр/с, всплеск 20.
  • OAuth provider/oauth/*: 1 запр/с, всплеск 60.
  • Public/api/contact: 0.2 запр/с, всплеск 10.
  • Feedback/api/feedback: три уровня друг над другом — одна отправка в 15 с, 10 в час, 36 в сутки.
  • Avatars — редирект аватара без аутентификации: 20 запр/с, всплеск 200.
  • SensitivePOST-запросы резервного копирования и восстановления: ~0.002 запр/с, всплеск 5.

Превышенный лимит возвращает 429 с заголовком Retry-After и стандартным JSON-конвертом ошибки, code: "rate_limited".