완전한 REST 엔드포인트 레퍼런스. 튜토리얼과 예제는 API 가이드를 참고하세요.
프로젝트 멤버가 웹 UI에서 할 수 있는 모든 것을 여기서 사용할 수 있습니다 — SPA는 이 동일한 API를 사용합니다. manager 역할을 요구하는 작업은 (manager)로 표시됩니다. 그 외 모든 것은 프로젝트 멤버십만 필요합니다(또는 (viewer)로 표시된 읽기의 경우 모든 접근 수준). 아래 표는 서버가 마운트하는 모든 라우트 그룹을 나열하며, 한 줄로 요약된 그룹은 실시간 openapi.json에 전부 기술되어 있습니다.
https://eastagiletracker.com/api/v1https://api.eastagiletracker.com/api/v1도 동일한 API를 제공합니다. multipart를 받는 몇몇 파일 업로드 엔드포인트를 제외하고, 모든 요청과 응답은 JSON입니다.
두 그룹은 /api/v1이 아니라 한 단계 위인 /api 아래에 있습니다: 인증 표면(/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 | 공개로 설정된 프로젝트에서는 누구나 | 보드 읽기: 스토리, 이터레이션, 검색, 스토리 및 에픽 활동(행위자 정보는 마스킹됨) |
| viewer | viewer, member, manager | 읽기(스토리 목록/조회, 검색, 지표, 익스포트 형식 목록) |
| member | member, manager | 모든 작업 항목 쓰기(스토리, 작업, 댓글, …), 이벤트 스트림 |
| manager | manager만 | 프로젝트 설정, 멤버십 관리, 에이전트 키, 삭제, 임포트, 익스포트 다운로드, 백업, 감사 로그 |
에이전트는 멤버와 같은 역할 — viewer, member, manager — 을 가지며, 키를 발급한 멤버의 역할이 상한입니다. 비멤버는 비공개 프로젝트 경로에서 403이 아니라 404 unfound_resource를 받으므로, 프로젝트 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 밖. |
인증 엔드포인트(/api/auth/*, /v1 밖)
섹션 제목: “인증 엔드포인트(/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 | 초대 토큰 → 이메일 해석 / 프로젝트 초대 수락(인증 후) |
계정 / 정체성
섹션 제목: “계정 / 정체성”이것들은 호출자에 대해 작동하며 유효한 키만 필요합니다(프로젝트 역할 없음).
| 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 | 2단계 인증(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당 속도 제한 |
참조 데이터(인증되지 않음)
섹션 제목: “참조 데이터(인증되지 않음)”스토리를 생성/추정할 때 사용되는 시드 조회. 안정적인 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} | 읽기, 이름 변경(이름 + 슬러그; 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=를 통한 스토리별 / 에픽별 활동. 접근 권한은 surface에 따라 다름(아래 참고) |
| GET | /projects/{id}/events | 커서 페이지네이션 이벤트 스트림 (member) — 이벤트 참고 |
감사 로그 쿼리 파라미터: event_type=(단일 타입 또는 쉼표로 구분한 목록), limit=(≤ 1000), before=(keyset 커서, ISO-8601 created_at), surface=(project_history, story_activities, epic_activities), target_id=(스토리/에픽 id — surface=story_activities 또는 epic_activities일 때 필수). 접근: 필터 없는 로그와 surface=project_history는 (manager)이며, story_activities / epic_activities는 프로젝트의 모든 멤버가 읽을 수 있고 공개 프로젝트에서는 익명으로도 읽을 수 있음(행위자 PII는 마스킹됨).
멤버, 에이전트, 에이전트 키
섹션 제목: “멤버, 에이전트, 에이전트 키”| 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로 승급(프로젝트 목록의 Make me owner 작업) |
| PUT | /projects/{id}/members/{mid}/anonymization | 이 프로젝트에서 멤버의 이름 / 이메일 / 아바타 마스킹 (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 역할이 필요합니다.
| 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 | delivered 스토리 거부 / 거부된 스토리를 started로 되돌림(rejected는 /transitions에서 종착 상태) |
| 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 — 3-상태 보관 필터; 사용 중단된 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 }를 반환합니다. 불법 이동 → details: { from, to, allowed }와 함께 422 invalid_transition.
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; 목록은 (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} | 에픽은 이름, 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} | 에픽별 진행: 번업, 처리량, 상태, 예측 (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 | 닫힌 이터레이션의 수락된 스토리, 페이지네이션 |
검색, 지표, 환경 설정
섹션 제목: “검색, 지표, 환경 설정”| 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); 에픽 지표는 위의 /analytics/epics 아래에 있음 |
| GET | /projects/{id}/backlog/grouping | Backlog의 예측된 이터레이션 그룹 (viewer) |
| GET / PUT | /projects/{id}/preferences | 이 프로젝트에 대한 여러분의 보드 환경 설정 — 모든 프로젝트 역할, 본인의 행만 |
이벤트
섹션 제목: “이벤트”| 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로 전달해 재개하세요.
앱 내 통합 알림 피드: 일급 알림 행(리뷰 요청, 스토리 활동, 초대 등)을 @멘션 수신함과 합쳐 최신순의 단일 스트림으로 제공합니다. 피드 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)
섹션 제목: “임포트 (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 본문 또는 dry_run=true multipart)는 아무 소스나 미리보기합니다: 파싱, 해석, 중복 제거를 수행하고 동일한 { 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)
섹션 제목: “백업과 복원 (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들은 sensitive 속도 제한 등급(아래)에 속합니다.
MCP와 OAuth 제공자
섹션 제목: “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
섹션 제목: “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는 항상 포함됩니다. 알려지지 않은 필드 이름은 details.fields에 위반한 이름과 함께 400 validation_failed를 반환합니다.
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 없음(대부분의 검증: 공백/길이/널 바이트/이메일) |
| 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 같은 추가 키를 포함합니다. 필드→메시지 맵은 없습니다.
{ "code": "validation_failed", "error": "unknown field(s): foo", "details": { "fields": ["foo"] } }속도 제한
섹션 제목: “속도 제한”클라이언트 IP당, 소수의 라우트에만 적용됩니다. 그 밖의 인증된 API 트래픽은 속도 제한되지 않습니다. 기본값(각 쌍은 지속 속도와 버스트이며, 운영자가 조정 가능):
- Auth —
/api/auth/*: 0.5 req/s, burst 20. - OAuth provider —
/oauth/*: 1 req/s, burst 60. - Public —
/api/contact: 0.2 req/s, burst 10. - Feedback —
/api/feedback: 세 겹의 등급 — 15초당 한 번 제출, 시간당 10회, 하루 36회. - Avatars — 인증되지 않은 아바타 리디렉션: 20 req/s, burst 200.
- Sensitive — 백업과 복원
POST: ~0.002 req/s, burst 5.
초과된 제한은 Retry-After 헤더와 표준 JSON 오류 봉투(code: "rate_limited")와 함께 429를 반환합니다.