完整的 REST 端点参考。关于教程和示例,请参阅 API 指南。
凡是一个项目成员能在 Web UI 中做的事,这里都有——SPA 消费的正是这同一套 API。需要所有者角色的操作标注为 (owner);其余的只需项目成员资格(或者,对于标注为 (viewer) 的读取,任何访问级别即可)。
https://eastagiletracker.com/api/v1https://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 是需要认证的——任何有效密钥都行,但它不限于项目范围(一个绑定到项目的智能体密钥也能访问它)。
三个级别门控着项目范围内的端点:
| Level | Who passes | Typical operations |
|---|---|---|
| viewer | viewer、member、owner | 读取(列出/获取故事、搜索、分析) |
| member | member、owner | 所有工作项写入(故事、任务、评论……) |
| owner | 仅 owner | 项目设置、成员管理、智能体密钥、删除、导入、审计日志 |
非成员在项目路径上会收到 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)+ 故事类型流转图。需要认证(任何有效密钥;不限于项目范围)。先调用它。 |
账户 / 身份
Section titled “账户 / 身份”这些作用于调用者本人,只需一个有效密钥(无需项目角色)。
| Method | Path | Description |
|---|---|---|
| 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 | 联系 + 应用内反馈 |
参考数据(无需认证)
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 | 某个尺度中的点数值 |
| Method | Path | Description |
|---|---|---|
| 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 |
成员与智能体密钥
Section titled “成员与智能体密钥”| Method | Path | Description |
|---|---|---|
| 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 角色。
| 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/bulk_transition | 一次流转多个故事(1–100) |
| POST | /projects/{id}/stories/bulk-delete | 删除多个故事 |
| POST | /projects/{id}/stories/bulk-duplicate | 复制多个故事 |
| POST | /projects/{id}/stories/{sid}/duplicate | 复制一个故事 |
Create(POST …/stories):{ "name" (required), "story_type": "feature|bug|chore|release", "description"?, "estimate"?, "current_state"?, "icebox"?, "labels"? }。labels 接受 ["auth"] 或 [{ "name": "auth" }];未知的标签会被创建。默认值:story_type=feature、current_state=unstarted。
Update(PUT …/stories/{sid}):相同的字段,全部可选,外加 "position"(float)和 "force_state_change"(bool)。
Transition(POST …/transitions):{ "to": "<state>", "reason"? }。字段是 to。返回 { story_id, state }。非法的移动 → 422 invalid_transition,附带 details: { from, to, allowed }。
Bulk transition(POST …/bulk_transition):{ "story_ids": [int,…] (1–100), "to": "<state>", "reason"? }。每个故事都被独立裁决;返回 { 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 / 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)。
| 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 | 列出迭代 (member) |
| POST | /projects/{id}/iterations | 创建一个手动迭代 (owner) |
| DELETE | /projects/{id}/iterations/{itid} | 删除一个迭代 (owner) |
搜索、分析、指标、偏好
Section titled “搜索、分析、指标、偏好”| Method | Path | Description |
|---|---|---|
| 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) |
Events
Section titled “Events”| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/events | 以游标分页的事件流 (viewer) |
查询参数:since=<event_id>、types=story.created,story.transitioned,comment.created,…、limit=、cursor=。响应包含 next_cursor。把你看到的最后一个 event_id 作为 since 传入以继续。
导入 (owner)
Section titled “导入 (owner)”| Method | Path | Description |
|---|---|---|
| POST | /projects/{id}/import (+ /json) | Multipart 上传。source= ∈ pivotal、jira、asana、gitlab、shortcut、trello、linear、plane、plane_json。 |
WebSocket
Section titled “WebSocket”wss://eastagiletracker.com/ws/control?token=<key>用于交互式 UI 远程控制({ "action": "get_state", "id": "req-1" })。它不是一个数据通道——所有读/写都走 REST。仅限单实例;不会在副本间扇出。
写入端点(POST、PUT、DELETE)接受一个 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 错误都有 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 被以一个不同的请求体重用 |
| 422 | invalid_transition | 非法的状态移动;details 携带 { from, to, allowed } |
| 500 | internal_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 错误信封)。