完整的 REST 端点参考。关于教程和示例,请参阅 API 指南。
凡是一个项目成员能在 Web UI 中做的事,这里都有——SPA 消费的正是这同一套 API。需要所有者角色的操作标注为 (manager);其余的只需项目成员资格(或者,对于标注为 (viewer) 的读取,任何访问级别即可)。下面的表格列出了服务器挂载的每一个路由组;那些仅用一行概括的,在实时的 openapi.json 中有完整描述。
https://eastagiletracker.com/api/v1https://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_ 开头,MCP 访问令牌以 ea_mcp_ 开头。见 API 指南 → 三种凭据。
无需认证的端点:/openapi.json、/docs、/api/auth/* 端点,以及参考数据查询(/story_types、/story_states、/effort_scales、/priority_scales)。/meta 是需要认证的——任何有效密钥都行,但它不限于项目范围(一个绑定到项目的智能体密钥也能访问它)。
四个级别门控着项目范围内的端点:
| Level | Who passes | Typical operations |
|---|---|---|
| public viewer | 任何人,在可见性为公开的项目上 | 看板的读取:故事、迭代、搜索、故事和 Epic 活动(行为者详情已脱敏) |
| viewer | viewer、member、manager | 读取(列出/获取故事、搜索、指标、导出格式列表) |
| member | member、manager | 所有工作项写入(故事、任务、评论……)、事件流 |
| manager | 仅 manager | 项目设置、成员管理、智能体密钥、删除、导入、导出下载、备份、审计日志 |
智能体持有与成员相同的角色——viewer、member 或 manager——上限为铸造该密钥的成员的角色。非成员在私有项目路径上会收到 404 unfound_resource(而非 403),因此项目 ID 无法被枚举。
| Method | Path | Description |
|---|---|---|
| GET | /openapi.json | 实时的 OpenAPI 3 规范,含请求体。无需认证。 |
| GET | /docs | Swagger UI。无需认证。 |
| GET | /meta | 调用者身份(auth.kind/key_id/agent_id/project_id)+ 故事类型流转图。需要认证(任何有效密钥;不限于项目范围)。先调用它。 |
| GET | /api/health · /api/config | 存活探测,以及该部署的公开配置(单组织模式、已启用的可选功能、实例名称)。无需认证,位于 /v1 之外。 |
Auth(/api/auth/*,位于 /v1 之外)
Section titled “Auth(/api/auth/*,位于 /v1 之外)”会话端点,除另有说明外均无需认证。由 SPA 驱动;脚本通常改用 API 密钥。
| Method | Path | Description |
|---|---|---|
| POST | /auth/register | 注册一个新账户——受 reCAPTCHA 保护;账户随后需通过 SMS 验证 |
| POST | /auth/sms/challenge · /auth/sms/status · /auth/sms/bypass | 发送 / 检查注册 SMS 验证码(bypass 由运维方门控) |
| GET | /auth/config | 该部署提供哪些登录方式 |
| POST | /auth/login | 用电子邮件 + 密码登录;返回一个会话 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 | 用 GitHub 或 Google 进行 OAuth 登录 |
| POST | /auth/refresh · /auth/refresh/revoke | 轮换刷新令牌 / 撤销它 |
| POST | /auth/logout | 登出(撤销刷新令牌) |
| POST | /auth/forgot-password · /auth/reset-password | 请求一封重置邮件 / 使用重置令牌 |
| POST | /auth/accept-invite/lookup · /auth/accept-invite | 把一个邀请令牌解析为电子邮件 / 接受项目邀请(认证之后) |
账户 / 身份
Section titled “账户 / 身份”这些作用于调用者本人,只需一个有效密钥(无需项目角色)。
| Method | Path | Description |
|---|---|---|
| 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} | 通行密钥的注册与移除 |
| 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-export | 你的数据的 GDPR 自导出 |
| GET | /me/consent · POST /me/consent | 读取 / 记录同意({ consent_type, granted }) |
| GET | /legal/pending · POST /legal/accept | 待处理的点击同意文档 / 记录接受 |
| GET / PUT | /agent/me | 智能体密钥自身的身份与资料,可由智能体读取和编辑(/me 在智能体侧的对应物) |
| POST | /api/contact · /api/feedback · /api/feedback/with-screenshot | 联系 + 应用内反馈。位于 /v1 之外;按 IP 限速 |
参考数据(无需认证)
Section titled “参考数据(无需认证)”创建/估算故事时使用的种子查询。ID 稳定。
| Method | Path | Description |
|---|---|---|
| GET | /story_types | feature、bug、chore、release(+ allow_points) |
| GET | /story_states | unstarted … accepted、rejected |
| GET | /effort_scales | 可用的估算尺度 |
| GET | /effort_scales/{scale_id}/values | 某个尺度中的点数值 |
| GET | /priority_scales · /priority_scales/{scale_id}/values | 优先级尺度及其取值(故事上的 priority_id 在此解析) |
仅限托管服务——自托管安装以单组织模式运行,不会挂载这些端点(组织列表除外)。角色为组织角色:owner、admin、member。
| Method | Path | Description |
|---|---|---|
| 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 | 在整个组织范围内遮蔽某成员的姓名 / 邮箱 / 头像 |
| 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 | 仅限 owner 的组织导出:一个包含 SQL 转储和所有附件的 zip,以作业方式运行 |
| Method | Path | Description |
|---|---|---|
| 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= 的按故事 / 按 Epic 活动;访问权限因 surface 而异,见下文 |
| GET | /projects/{id}/events | 以游标分页的事件流 (member)——见 Events |
审计日志查询参数:event_type=(单个类型或逗号分隔列表)、limit=(≤ 1000)、before=(keyset 游标,ISO-8601 格式的 created_at)、surface=(project_history、story_activities、epic_activities)、target_id=(故事/Epic 的 id——当 surface=story_activities 或 epic_activities 时必填)。访问权限:未过滤日志与 surface=project_history 为 (manager);story_activities / epic_activities 项目任意成员均可读取,公共项目上也可匿名读取,行为者的 PII 会被脱敏。
成员、智能体与智能体密钥
Section titled “成员、智能体与智能体密钥”| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/memberships | 列出成员 (viewer) |
| POST | /projects/{id}/memberships | 按电子邮件邀请一名成员 (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/join | 组织的 owner 或 admin 以 manager 身份加入其组织内的某个项目,或把自己提升为 manager(项目列表上的把我设为所有者操作) |
| PUT | /projects/{id}/members/{mid}/anonymization | 在此项目上遮蔽某成员的姓名 / 邮箱 / 头像 (manager) |
| GET / POST | /projects/{id}/agent_keys | 列出 / 铸造智能体密钥——manager,或项目的创建者角色策略所允许的角色 |
| 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 角色。
| Method | Path | Description |
|---|---|---|
| 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} | 该故事的 Epic 归属 |
| 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 拒绝)。故事写入还会依据故事的 ETag 检查 If-Match;不匹配则为 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)。
| Method | Path | Body / 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_type ∈ relates_to、duplicates、blocks、is_blocked_by、pull_request、branch、other;GitHub 的 /pull/ 和 /tree/ URL 会被自动判定类型 |
| 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;list 为 (viewer) |
| GET / POST | /projects/{id}/stories/{sid}/link-attachments · DELETE …/link-attachments/{laid} | 链接附件——与文件附件放在一起、而非作为代码链接保存的外部 URL |
| GET | /attachments/{token} · /api/avatars/{token} | 以令牌寻址读取附件或头像——即 API 返回的那些 URL;无需 X-TrackerToken |
与故事形态相同,只是没有状态机。写入需 member,读取为 (viewer)。
| Method | Path | Description |
|---|---|---|
| GET / POST | /projects/{id}/epics · GET / PUT / DELETE …/epics/{eid} | Epic 带有名称、Markdown 描述,以及一个把其故事连接起来的底层标签 |
| GET / POST / PUT / DELETE | …/epics/{eid}/comments · …/comments/{cid} | Epic 评论 |
| GET / POST / DELETE | …/epics/{eid}/owners · …/followers(+ /agents/{aid} 变体) | 负责人与关注者,成员或智能体——Epic 的负责人会传播到其故事 |
| GET / POST / DELETE | …/epics/{eid}/attachments(+ /json)· …/link-attachments | 附件,上限与故事相同 |
| GET | /projects/{id}/analytics/epics · …/analytics/epics/{eid} | 按 Epic 的进度:燃起、吞吐量、健康度、预测 (viewer) |
写入需 member,读取为 (viewer)。
| Method | Path | Description |
|---|---|---|
| GET / POST | /projects/{id}/labels | 列出 / 创建一个标签 |
| PUT / DELETE | /projects/{id}/labels/{lid} | 更新 / 删除一个标签 |
| POST | /projects/{id}/labels/{lid}/archive | 归档(软隐藏)一个标签 |
读取对任何项目角色开放,在公开项目上还可匿名读取。
| Method | Path | Description |
|---|---|---|
| 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 | 覆盖单个迭代的速率而不改变项目策略 (manager) |
| GET | /projects/{id}/iterations/{itid}/done-stories | 某个已关闭迭代中已接受的故事,可分页 |
搜索、指标、偏好
Section titled “搜索、指标、偏好”| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/search?q=… | 强大的搜索——全文 + 分面 / 日期范围 / 人员限定符(GitHub 风格的 DSL);返回 { 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);Epic 指标在上文的 /analytics/epics 之下 |
| GET | /projects/{id}/backlog/grouping | Backlog 投影出的迭代分组 (viewer) |
| GET / PUT | /projects/{id}/preferences | 你针对此项目的看板偏好——任何项目角色,仅限你自己的那一行 |
Events
Section titled “Events”| Method | Path | Description |
|---|---|---|
| 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_* 密钥读取其代理侧行。
| Method | Path | Description |
|---|---|---|
| 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 | 实时推送 — Server-Sent Events(text/event-stream);见下文 |
stream 端点不是 JSON 端点,因此不在 OpenAPI 规范中:它保持连接打开,每当有新内容到达时发送一个不带载荷的帧({"type":"notification","kind":…}),提示客户端重新拉取信息流。连接在服务端 45 分钟后关闭 — 请重新连接并重新认证。仅限成员会话和 ea_user_* 密钥;ea_agent_* 密钥会收到 403。
导入 (manager)
Section titled “导入 (manager)”| Method | Path | Description |
|---|---|---|
| POST | /projects/{id}/import | 文件来源:source= ∈ pivotal、jira、asana、gitlab、shortcut、trello、linear、plane、plane_json、eat。Multipart file=。同步——以结果计数作答。 |
| POST | /projects/{id}/import/json | JSON 请求体;source=github 无需文件——owner、repo、可选的 token,以及可选启用的开关 include_pull_requests / include_milestones / include_releases / include_dependencies;文件来源则发送 file_base64。异步:返回 202 { import_id, status }。服务器通过 GitHub 的 GraphQL API 抓取,而该 API 拒绝匿名调用方,因此总会有一个令牌抵达 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 体或 multipart 的 dry_run=true)可预览任意来源:解析、解析映射、去重,产生相同的 { imported, skipped, errors, unmatched } 计数,然后回滚——什么都不会写入。上限:体积 10 MiB,以及基于文件的来源单次导入 5,000 个故事(超出任一者 → 400,不写入任何内容)。GitHub 来源没有上限——它分块提交,而不是放在单个事务里。重新导入按来源 id 幂等——已导入的行会被跳过,而非重复。
| Method | Path | Description |
|---|---|---|
| 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 互通格式会忽略这些参数。
备份与恢复 (manager)
Section titled “备份与恢复 (manager)”| Method | Path | Description |
|---|---|---|
| GET / POST | /projects/{id}/backups · GET …/backups/{snapshot_id} · …/backups/health | 列出快照、立即创建一个、读取一个,以及保留期健康状况摘要 |
| GET / POST | /projects/{id}/restores · POST …/restores/partial · GET …/restores/{restore_id} | 恢复整个快照,或从某个快照恢复选定的表,并轮询恢复进度 |
这些 POST 位于敏感速率限制层级(见下文)。
MCP 与 OAuth 提供方
Section titled “MCP 与 OAuth 提供方”East Agile Tracker 是面向 MCP 客户端的 OAuth 2.1 提供方。客户端在 /.well-known/oauth-authorization-server 和 /.well-known/oauth-protected-resource/mcp 发现它,把你送到 /oauth/authorize(同意页面),在 /oauth/token 兑换授权码,然后用得到的 ea_mcp_* 令牌在 /mcp 上使用 MCP。授权可在 /me/oauth_grants 列出和撤销。提供方端点有自己的速率限制层级。
WebSocket
Section titled “WebSocket”wss://eastagiletracker.com/ws/control?token=<session JWT>用于交互式 UI 远程控制({ "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/*,或 /attachments 路径上的 multipart 上传。未能给出领域答案就中止的响应从不被缓存——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"] } }| Status | code | When |
|---|---|---|
| 400 | invalid_parameter | 输入有误;消息在 error 中,无 details(大多数校验:空白/长度/null 字节/电子邮件) |
| 400 | validation_failed | 结构化的输入错误;details.fields 是一个由出错字段名组成的数组 |
| 401 | unauthenticated | 令牌缺失/无效 |
| 403 | unauthorized_operation | 已认证但角色不足 |
| 404 | unfound_resource | 未找到——也会返回给非成员 |
| 409 | conflict | 资源冲突(例如重复) |
| 409 | idempotency_conflict | Idempotency-Key 被以一个不同的请求体重用 |
| 409 | stale_write · import_already_running | 故事自你的 expected_updated_at 之后已发生变化 · 已有一个导入在进行中 |
| 412 | precondition_failed | If-Match 与资源当前的 ETag 不匹配;details 携带 expected 和 current |
| 413 | request_too_large | 请求体超出该路由的大小限制 |
| 422 | invalid_transition | 非法的状态移动;details 携带 { from, to, allowed } |
| 429 | rate_limited | 此 IP 在受限速路由上的请求过多;带 Retry-After 请求头 |
| 500 | internal_error | 服务端故障——通用消息;可安全重试 |
| 503 | not_configured | 该部署缺少此路由所需的集成(SMS、对象存储……) |
details.fields 是一个由字段名组成的 JSON 数组(例如 ["to"]),有时还带有额外的键,比如 max。没有 field→message 的映射。
{ "code": "validation_failed", "error": "unknown field(s): foo", "details": { "fields": ["foo"] } }按客户端 IP,仅限少数路由;其余经过认证的 API 流量不做速率限制。默认值(每对数值为持续速率与突发量,可由运维方调整):
- Auth——
/api/auth/*:0.5 req/s,突发 20。 - OAuth provider——
/oauth/*:1 req/s,突发 60。 - Public——
/api/contact:0.2 req/s,突发 10。 - Feedback——
/api/feedback:三层叠加——每 15 秒一次提交、每小时 10 次、每天 36 次。 - Avatars——无需认证的头像重定向:20 req/s,突发 200。
- Sensitive——备份与恢复的
POST:约 0.002 req/s,突发 5。
超出限制会返回 429,附带一个 Retry-After 请求头和标准的 JSON 错误信封,code: "rate_limited"。