跳转到内容

API 规范

East Agile Tracker API 的完整 REST 端点参考:认证与角色、项目、故事、史诗、迭代、事件、导入与导出。

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

凡是一个项目成员能在 Web UI 中做的事,这里都有——SPA 消费的正是这同一套 API。需要 manager 角色的操作标注为 (manager);其余的只需项目成员资格(或者,对于标注为 (viewer) 的读取,任何访问级别即可)。下面的表格列出了服务器挂载的每一个路由组;那些仅用一行概括的,在实时的 openapi.json 中有完整描述。

https://api.eastagiletracker.com/api/v1

API 有自己的主机:eastagiletracker.com 提供 Web 应用,对 /api/v1/… 则以 404 作答。所有请求和响应都是 JSON,少数接受 multipart 的文件上传端点除外。

有两组端点位于上一层,挂在 /api 而非 /api/v1 之下:认证界面(/api/auth/*)和公共联系表单(/api/contact)。它们的 /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 是需要认证的——任何有效密钥都行,但它不限于项目范围(一个绑定到项目的智能体密钥也能访问它)。

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

LevelWho passesTypical operations
public viewer任何人,在可见性为公开的项目上看板的读取:故事、迭代、搜索、故事和 Epic 活动(行为者详情已脱敏)
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用电子邮件 + 密码登录;返回一个会话 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把一个邀请令牌解析为电子邮件 / 接受项目邀请(认证之后)
POST/auth/oauth/link/preview · …/link/confirm · /auth/oauth/merge/preview · …/merge/confirm把一个 GitHub 或 Google 账户连接到已有账户,或合并两个账户
POST/auth/email_addresses/primary/preview · …/primary/confirm通过邮件中的确认链接完成主邮箱的更改

这些作用于调用者本人,无需项目角色。它们需要一个成员身份——智能体密钥会得到 401(智能体使用 /agent/me)。更改凭据的调用——密码、TOTP、通行密钥、账户删除、邮箱更改、令牌重新生成——需要浏览器会话;API 密钥会得到 403。

MethodPathDescription
GET/me当前用户资料
PUT/me更新资料
DELETE/me删除账户——当你是某个组织、或某个仍有其他成员的项目的唯一所有者时会被拒绝
GET/me/deletion-impact删除账户会移除什么,以及什么会阻止删除
PUT/me/password更改密码
PUT/me/settings更新设置(主题、通知偏好)
POST / DELETE/me/avatar上传(multipart)/ 移除你的头像
POST/me/api-token/regenerate登出所有会话并返回一个新的会话令牌;API 密钥不受影响
GET / POST/me/email_addresses · GET …/email_addresses/offers · PUT …/email_addresses/primary你的电子邮件地址;把某个已连接提供方的地址采纳为备用地址;请求更改主邮箱(通过邮件确认)
GET/me/oauth_identities · DELETE …/oauth_identities/{provider} · POST …/oauth_identities/{provider}/link已连接的登录账户(GitHub、Google):列出、断开、发起连接
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/v1/feedback · /api/v1/feedback/with-screenshot联系 + 应用内反馈;按 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 在此解析)

仅限托管服务——自托管安装以单组织模式运行,不会挂载这些端点(组织列表除外)。角色为组织角色: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 角色移交给另一名成员
GET/organization-invitations/{token} · POST …/{token}/accept解析 / 接受一封邮件中的组织邀请
GET/organizations/{oid}/export · POST …/export/jobs · GET …/export/jobs/{job_id} · …/export/jobs/{job_id}/download仅限 owner 的组织导出:一个包含 SQL 转储和所有附件的 zip,以作业方式运行
MethodPathDescription
GET/projects列出你的项目(limit ≤ 200)
POST/projects创建一个项目
GET/projects/{id}获取项目详情 (viewer)
PUT/projects/{id}更新项目设置 (manager)
DELETE/projects/{id}删除一个项目 (manager)
POST / DELETE/projects/{id}/pin在你的项目列表上置顶 / 取消置顶该项目
POST/projects/{id}/transfer-organization把项目移到另一个组织——调用者必须是该项目的管理者,并且是目标组织的组织 owner 或 admin
POST/projects/{id}/slack/test向项目的 Slack 信息流发送一条测试消息 (manager)
GET / POST/projects/{id}/showcase_claim_eligibility · /projects/{id}/showcase_claim · /projects/{id}/showcase_seed公开展示项目:检查你是否能认领一个、认领它、为它填充种子数据(填充种子数据仅限产品管理员;其他人会得到 404)
GET/projects/{id}/audit-log审计日志读取——项目历史,以及通过 surface= 的按故事 / 按 Epic 活动;访问权限因 surface 而异,见下文
GET/projects/{id}/events以游标分页的事件流 (member)——见 Events

审计日志查询参数:event_type=(单个类型或逗号分隔列表)、limit=(默认 50,上限钳制为 1000)、before=(keyset 游标 <ISO-8601>|<audit_id>——传入上一页的 next_cursor)、surface=(project_history、story_activities、epic_activities)、target_id=(故事/Epic 的 id——当 surface=story_activities 或 epic_activities 时必填)。访问权限:未过滤日志为 (manager);surface=project_history 为 (viewer),当项目隐藏成员邮箱时会脱敏;story_activities / epic_activities 项目任意成员均可读取,公共项目上也可匿名读取,行为者的 PII 会被脱敏。

MethodPathDescription
GET/projects/{id}/memberships列出成员 (viewer)
POST/projects/{id}/memberships按电子邮件邀请一名成员 (manager)
PUT/projects/{id}/memberships/{mid}更新角色 (manager)
DELETE/projects/{id}/memberships/{mid}移除一名成员 (manager)
POST/projects/{id}/memberships/bulk-role · …/memberships/bulk-remove一次更改多名成员的角色或将其移除 (manager)
GET/projects/{id}/addable-members · POST /projects/{id}/members/add-existing尚未加入项目的组织成员 / 无需邮件邀请直接添加一名——(manager) 且为组织 owner 或 admin
POST/projects/{id}/members/join组织的 owner 或 admin 以 manager 身份加入其组织内的某个项目,或把自己提升为 manager(项目列表上的把我设为所有者操作)
GET / POST/projects/{id}/agent_keys列出 (manager) / 铸造智能体密钥——角色在项目创建者角色策略之内的人;智能体密钥不能铸造
PUT / DELETE/projects/{id}/agent_keys/{kid}编辑密钥的名称、角色或响应提示 / 撤销它 (manager)
GET/projects/{id}/agent_keys/onboarding入门套件:面向常见智能体客户端的提示词和配置文件
GET/projects/{id}/agents · GET / PUT / DELETE /projects/{id}/agents/{aid}项目的智能体及其资料(名称、首字母、描述、颜色);DELETE 撤销该智能体及其所有密钥——manager 或该智能体的创建者
POST/projects/{id}/agents/{aid}/rotate-key · /projects/{id}/agents/{aid}/avatar轮换某个智能体的密钥(保留身份与历史) / 上传其头像

所有故事写入都需要 member 角色。创建带有回溯日期、导入来源(import_source / import_external_id)或外部负责人 / 请求者的故事需要 (manager)。

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复制一个故事
POST / DELETE/projects/{id}/stories/{sid}/epics · …/epics/{eid}该故事的 Epic 归属
POST/projects/{id}/stories/bulk-unarchive取消归档多个故事
GET/short-links/{code} · /story-references把一个 /s/<code> 短链接解析为其故事 / 把最多 100 个引用——codes=(逗号分隔的短链接代码)和 ids=(逗号分隔的 project_id:story_id)——解析为调用者可读的故事

故事列表查询参数: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_version"(整数,即故事的 version——若描述在该版本之后发生了变化,保存描述会被以 409 stale_write 拒绝)。旧的 "expected_updated_at"(RFC 3339)已弃用,仅在下一个版本内继续有效:若随描述一起发送且未带 expected_version,除非它等于故事的 updated_at,否则保存会被以 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)。删除附件或链接附件需要其上传者或 (manager)。

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? };更新:{ url, 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}{ label_id }——一个已有的项目标签;用 POST /projects/{id}/labels 创建标签,或在故事的 PUT 中发送 labels
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/v1/avatars/{token}以令牌寻址读取附件或头像——即 API 返回的那些 URL;无需 X-TrackerToken

与故事形态相同,只是没有状态机。写入以及评论、负责人和关注者列表需 member;Epic 列表、单个 Epic 和附件列表为 (viewer)。

MethodPathDescription
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)。

MethodPathDescription
GET / POST/projects/{id}/labels列出 / 创建一个标签
GET / 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种子写入将创建的迭代(或已存在时的当前迭代)的时间窗口、预计速率和 Backlog 填充数 (member)
POST/projects/{id}/iterations创建一个手动迭代 (manager)
DELETE/projects/{id}/iterations/{itid}删除一个迭代 (manager)
PUT/projects/{id}/iterations/{itid}/velocity覆盖单个迭代的速率而不改变项目策略 (manager)
GET/projects/{id}/iterations/{itid}/done-stories某个已关闭迭代中已接受的故事,可分页
MethodPathDescription
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/groupingBacklog 投影出的迭代分组 (viewer)
GET / PUT/projects/{id}/preferences你针对此项目的看板偏好——任何项目角色,仅限你自己的那一行
MethodPathDescription
GET/projects/{id}/events以游标分页的事件流 (member)——viewer 会得到 403

查询参数:since_id=<event_id>(since 是旧的别名;两者同时发送时以 since_id 为准)、types=story.created,story.transitioned,comment.added,…、limit=(默认 100,≤ 500)、cursor=。响应为 { items, next_since_id, next_cursor }:保存 next_since_id,并把它作为 since_id 传回以继续;在最后一页 next_cursor 为 null。

应用内统一的通知信息流:一等公民的通知行(评审请求、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实时推送 — 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,以及 include_comments(默认 true);文件来源则发送 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 时带有结果计数

每个项目同一时间只运行一个 JSON 导入;在一个导入进行中再次 POST …/import/json 会得到 409 import_already_running。带上 check_unpointed_estimates: true(文件来源;对 github 忽略)时,包含项目不计点的缺陷或杂务估算的文件,会在任何作业开始之前被 409 import_unpointed_estimates 和 details.count 拒绝;不带该标志时,导入会丢弃这些估算,并在结果的 warnings 中报告 { code: "estimates_unpointed_dropped", count }。dry_run: true(JSON 体或 multipart 的 dry_run=true)可预览任意来源:解析、解析映射、去重,产生相同的 { imported, skipped, errors, unmatched } 计数,然后回滚——什么都不会写入。上限:基于文件的来源,文件 10 MiB(400 import_too_large),单次导入 5,000 个故事(400 import_too_many_records);不写入任何内容。超过 16 MiB 的请求体会更早被以 413 request_too_large 拒绝。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列出快照、立即创建一个、读取一个,以及保留期健康状况摘要
POST/projects/{id}/restores · POST …/restores/partial · GET …/restores/{restore_id}恢复整个快照,或从某个快照恢复选定的表,并轮询恢复进度

这些 POST 位于敏感速率限制层级(见下文)。

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 列出和撤销。提供方端点有自己的速率限制层级。

写入端点(POST、PUT、DELETE)接受一个 Idempotency-Key 请求头。相同密钥 + 相同请求体会重放缓存的响应(24 小时窗口);相同密钥 + 一个不同的请求体会返回 409 idempotency_conflict。该键的作用域限于发送它的凭据。不适用于 GET/HEAD/OPTIONS、/openapi.json 和 /docs、/api/auth/*、/mcp、任何包含 /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。搜索用 limit(≤ 1000)和 offset 而非游标分页,返回 { results, total, limit, offset }。

一个不得不截断响应的普通列表(无 cursor/limit)会通过响应头说明这一点——X-Tracker-Pagination-Truncated、X-Tracker-Pagination-Limit、X-Tracker-Pagination-Offset 和 X-Tracker-Pagination-Next-Offset;把最后一个作为 offset= 传回即可获取下一页。没有总数响应头。

故事、评论和项目列表接受 fields=(逗号分隔),以仅返回特定字段。资源的 id 始终被包含(story_id、story_comment_id 与 comment_id、project_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 中。字段校验(空白/长度/null 字节/电子邮件)会附带 details.fields 和 details.constraint;只有普通的 bad request 错误不带 details
400validation_failed结构化的输入错误;details.fields 是一个由出错字段名组成的数组
400领域代码路由特定的拒绝,带有自己的代码且没有 details——例如 unsupported_export_format、import_too_large、import_unknown_source
401unauthenticated令牌缺失/无效
403unauthorized_operation已认证但角色不足
404unfound_resource未找到——也会返回给非成员
409conflict资源冲突(例如重复)
409idempotency_conflictIdempotency-Key 被以一个不同的请求体重用
409stale_write · import_already_running · import_unpointed_estimates描述在你的 expected_version 之后已发生变化(或 expected_updated_at 不再匹配)· 已有一个导入在进行中 · 文件中含有项目不计点的缺陷或杂务估算(details.count)
412precondition_failedIf-Match 与资源当前的 ETag 不匹配;details 携带 expected 和 current
413request_too_large请求体超出该路由的大小限制
422invalid_transition非法的状态移动;details 携带 { from, to, allowed }
429rate_limited此 IP 在受限速路由上的请求过多;带 Retry-After 请求头
500internal_error服务端故障——通用消息;可安全重试
502upstream_error该路由依赖的某个服务(例如 GitHub)失败
503not_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/v1/feedback 和 /api/v1/feedback/with-screenshot:三层叠加——每 15 秒一次提交、每小时 10 次、每天 36 次。
  • Avatars——无需认证的头像重定向:20 req/s,突发 200。
  • Sensitive——备份与恢复的 POST:约 0.002 req/s,突发 5。

超出限制会返回 429,附带一个 Retry-After 请求头和标准的 JSON 错误信封,code: "rate_limited"。