跳转到内容

API 规范

完整的 REST 端点参考。关于教程和示例,请参阅 API 指南

凡是一个项目成员能在 Web UI 中做的事,这里都有——SPA 消费的正是这同一套 API。需要所有者角色的操作标注为 (owner);其余的只需项目成员资格(或者,对于标注为 (viewer) 的读取,任何访问级别即可)。

https://eastagiletracker.com/api/v1

https://api.eastagiletracker.com/api/v1 提供完全相同的 API。所有请求和响应都是 JSON,少数接受 multipart 的文件上传端点除外。

每个经过认证的请求都通过以下之一发送一个 API 密钥:

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

用户密钥以 ea_user_ 开头。智能体密钥以 ea_agent_ 开头。见 API 指南 → 两种密钥

无需认证的端点:/openapi.json/docs/auth/* 端点,以及参考数据查询(/story_types/story_states/effort_scales……)。/meta需要认证的——任何有效密钥都行,但它不限于项目范围(一个绑定到项目的智能体密钥也能访问它)。

三个级别门控着项目范围内的端点:

LevelWho passesTypical operations
viewerviewer、member、owner读取(列出/获取故事、搜索、分析)
membermember、owner所有工作项写入(故事、任务、评论……)
owner仅 owner项目设置、成员管理、智能体密钥、删除、导入、审计日志

非成员在项目路径上会收到 404 unfound_resource(而非 403),因此项目 ID 无法被枚举。

MethodPathDescription
GET/openapi.json实时的 OpenAPI 3 规范。无需认证。
GET/docsSwagger UI。无需认证。
GET/meta调用者身份(auth.kind/key_id/agent_id/project_id)+ 故事类型流转图。需要认证(任何有效密钥;不限于项目范围)。先调用它。

这些作用于调用者本人,只需一个有效密钥(无需项目角色)。

MethodPathDescription
POST/auth/register注册一个新账户
POST/auth/login登录,返回一个会话令牌
POST/auth/logout登出
POST/auth/forgot-password请求一封密码重置邮件
POST/auth/reset-password用一个重置令牌设置新密码
GET/auth/verify-email验证一个电子邮件地址
POST/auth/accept-invite/lookup把一个邀请令牌解析为电子邮件(无需认证)
POST/auth/accept-invite接受一个项目邀请(认证之后)
GET/me当前用户资料
PUT/me更新资料
DELETE/me删除账户
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/activity你在所有项目中的活动
GET/me/data-export你的数据的 GDPR 自导出
GET/me/consent · POST /me/consent读取 / 记录同意({ consent_type, granted }
GET/legal/pending · POST /legal/accept待处理的点击同意文档 / 记录接受
POST/contact · POST /feedback · POST /feedback/with-screenshot联系 + 应用内反馈

创建/估算故事时使用的种子查询。ID 稳定。

MethodPathDescription
GET/story_typesfeature、bug、chore、release(+ allow_points
GET/story_statesunstarted … accepted、rejected
GET/effort_scales可用的估算尺度
GET/effort_scales/{scale_id}/values某个尺度中的点数值
MethodPathDescription
GET/projects列出你的项目
POST/projects创建一个项目
GET/projects/{id}获取项目详情 (viewer)
PUT/projects/{id}更新项目设置 (owner)
DELETE/projects/{id}删除一个项目 (owner)
GET/projects/{id}/audit-log项目活动流 (owner)
GET/projects/{id}/events以游标分页的事件流 (viewer)——见 Events
MethodPathDescription
GET/projects/{id}/memberships列出成员 (viewer)
POST/projects/{id}/memberships按电子邮件邀请一名成员 (owner)
PUT/projects/{id}/memberships/{mid}更新角色 (owner)
DELETE/projects/{id}/memberships/{mid}移除一名成员 (owner)
GET / POST/projects/{id}/agent_keys列出 / 铸造智能体密钥 (owner)
DELETE/projects/{id}/agent_keys/{kid}撤销一个智能体密钥 (owner)

所有故事写入都需要 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/bulk_transition一次流转多个故事(1–100)
POST/projects/{id}/stories/bulk-delete删除多个故事
POST/projects/{id}/stories/bulk-duplicate复制多个故事
POST/projects/{id}/stories/{sid}/duplicate复制一个故事

CreatePOST …/stories):{ "name" (required), "story_type": "feature|bug|chore|release", "description"?, "estimate"?, "current_state"?, "icebox"?, "labels"? }labels 接受 ["auth"][{ "name": "auth" }];未知的标签会被创建。默认值:story_type=featurecurrent_state=unstarted

UpdatePUT …/stories/{sid}):相同的字段,全部可选,外加 "position"(float)和 "force_state_change"(bool)。

TransitionPOST …/transitions):{ "to": "<state>", "reason"? }。字段是 to。返回 { story_id, state }。非法的移动 → 422 invalid_transition,附带 details: { from, to, allowed }

Bulk transitionPOST …/bulk_transition):{ "story_ids": [int,…] (1–100), "to": "<state>", "reason"? }。每个故事都被独立裁决;返回 { 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 / POST/projects/{id}/stories/{sid}/blockers · PUT/DELETE …/blockers/{bid}{ blocker_desc, resolved? }
GET / POST/projects/{id}/stories/{sid}/links · DELETE …/links/{lid}{ url, link_type?, title? }
GET / POST/projects/{id}/stories/{sid}/reviews · PUT/DELETE …/reviews/{rid}{ reviewer_id? / reviewer_agent_id?, 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 上传(每个 ≤ 2 GB);list 为 (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列出迭代 (member)
POST/projects/{id}/iterations创建一个手动迭代 (owner)
DELETE/projects/{id}/iterations/{itid}删除一个迭代 (owner)
MethodPathDescription
GET/projects/{id}/search?query=…筛选语法搜索 (viewer)——见指南
GET/projects/{id}/analytics/{overview,iteration,releases,activity,cycle-time,projections}分析 (viewer)。迭代下钻接受 ?iteration_id=
GET/projects/{id}/metrics/{velocity,burndown,story-types}指标 (viewer)
GET / PUT/projects/{id}/preferences你针对此项目的看板偏好 (member)
MethodPathDescription
GET/projects/{id}/events以游标分页的事件流 (viewer)

查询参数:since=<event_id>types=story.created,story.transitioned,comment.created,…limit=cursor=。响应包含 next_cursor。把你看到的最后一个 event_id 作为 since 传入以继续。

MethodPathDescription
POST/projects/{id}/import (+ /json)Multipart 上传。source=pivotaljiraasanagitlabshortcuttrellolinearplaneplane_json
wss://eastagiletracker.com/ws/control?token=<key>

用于交互式 UI 远程控制({ "action": "get_state", "id": "req-1" })。它不是一个数据通道——所有读/写都走 REST。仅限单实例;不会在副本间扇出。

写入端点(POSTPUTDELETE)接受一个 Idempotency-Key 请求头。相同密钥 + 相同请求体会重放缓存的响应(24 小时窗口);相同密钥 + 一个不同的请求体会返回 409 idempotency_conflict。不适用于 GET/HEAD/OPTIONS/auth/*/attachments 路径。5xx 响应从不被缓存——重试会抵达处理器。

列表端点接受 cursor=<opaque>limit=<n≤200>。设置后,响应为 { "items": [...], "next_cursor": "<str|null>" };把 next_cursor 传回以翻页。某些列表还会返回一个 X-Tracker-Pagination-Total 请求头。

列表端点接受 fields=(逗号分隔),以仅返回特定字段。story_id 始终被包含;一个未知的字段名会返回 400 validation_failed,并在 details.fields 中给出出错的名称。

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

每个 JSON 错误都有 codeerror;有些会加上 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 字节/电子邮件)
400validation_failed结构化的输入错误;details.fields 是一个由出错字段名组成的数组
401unauthenticated令牌缺失/无效
403unauthorized_operation已认证但角色不足
404unfound_resource未找到——也会返回给非成员
409conflict资源冲突(例如重复)
409idempotency_conflictIdempotency-Key 被以一个不同的请求体重用
422invalid_transition非法的状态移动;details 携带 { from, to, allowed }
500internal_error服务端故障——通用消息;可安全重试

details.fields 是一个由字段名组成的 JSON 数组(例如 ["to"]),有时还带有额外的键,比如 max。没有 field→message 的映射。

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

按密钥(无需认证的端点则按 IP),GCRA 令牌桶:

  • Auth——/auth/*:0.5 req/s,突发 20。
  • Public——/contact/feedback:0.2 req/s,突发 10。
  • Sensitive——密码重置:约 0.002 req/s,突发 5。

超出限制会返回 429 Too Many Requests,附带一个 Retry-After 请求头和一个纯文本响应体(而非 JSON 错误信封)。