{"openapi":"3.0.3","info":{"title":"ACM Board API","version":"1.0.0","description":"Tạo và quản lý task từ bên ngoài (AI agent, script). Xác thực bằng API key: header `Authorization: Bearer acm_...` (tạo ở menu tài khoản → API key). Key hoạt động với tư cách người tạo ra nó: task tạo qua API được ghi tên người đó kèm nhãn \"qua API\". Dùng header `Idempotency-Key` khi tạo task để gọi lại an toàn (không tạo trùng). Giới hạn 120 request/phút mỗi key."},"servers":[{"url":"https://internal-ops.acmsystem.dev/api/v1"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"API key dạng acm_..."}},"schemas":{"User":{"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"name":{"type":"string"}}}}},"paths":{"/me":{"get":{"summary":"Key này thuộc về ai, quyền gì","responses":{"200":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/users":{"get":{"summary":"Danh sách thành viên (để chọn assignees)","responses":{"200":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/sprints":{"get":{"summary":"Danh sách sprint, sprint đang chạy có current=true","responses":{"200":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/tasks":{"get":{"summary":"Liệt kê task (chưa lưu trữ), mới nhất trước","parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"type","in":"query","schema":{"type":"string"}},{"name":"priority","in":"query","schema":{"type":"string"}},{"name":"sprint","in":"query","schema":{"type":"string"},"description":"tên sprint / current / none"},{"name":"assignee","in":"query","schema":{"type":"string"},"description":"username, hoặc \"me\", hoặc \"none\" (chưa giao)"},{"name":"q","in":"query","schema":{"type":"string"},"description":"tìm trong tiêu đề và mô tả"},{"name":"updatedSince","in":"query","schema":{"type":"string"},"description":"ISO 8601: chỉ task đổi sau thời điểm này"},{"name":"limit","in":"query","schema":{"type":"string"},"description":"tối đa 500, mặc định 200"}],"responses":{"200":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"priority":{"type":"string"},"assignees":{"type":"array","items":{"$ref":"#/components/schemas/User"}},"creator":{"$ref":"#/components/schemas/User"},"sprint":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"startAt":{"type":"string","nullable":true},"dueAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"completedAt":{"type":"string","nullable":true},"commentCount":{"type":"integer"},"attachmentCount":{"type":"integer"}}}}}}}}}}},"post":{"summary":"Tạo task (cần key quyền write)","parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string","maxLength":200},"description":"Cùng key + cùng key API → trả lại task đã tạo thay vì tạo trùng"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":200},"description":{"type":"string","maxLength":20000},"status":{"type":"string","enum":["backlog","todo","doing","done"],"description":"Mặc định todo khi tạo. backlog = chưa vào sprint; todo/doing/done tự vào sprint đang chạy nếu có."},"type":{"type":"string","enum":["task","bug","meeting"],"default":"task"},"priority":{"type":"string","enum":["low","medium","high"],"default":"medium"},"startAt":{"type":"string","format":"date-time","nullable":true,"description":"Ngày bắt đầu (ISO 8601 kèm múi giờ); null = xoá. Không được sau dueAt."},"dueAt":{"type":"string","format":"date-time","nullable":true,"description":"Hạn chót (ISO 8601 kèm múi giờ, VD 2026-10-05T17:00:00+07:00); null = xoá."},"assignees":{"type":"array","items":{"type":"string"},"maxItems":10,"description":"Username hoặc tên hiển thị của thành viên (xem GET /users). Khi sửa: thay cả danh sách."},"sprint":{"type":"string","nullable":true,"description":"Tên sprint, \"current\" (sprint đang chạy), hoặc id; null = không thuộc sprint (Backlog)."}}}}}},"responses":{"200":{"description":"Idempotency-Key đã dùng: trả lại task cũ"},"201":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"priority":{"type":"string"},"assignees":{"type":"array","items":{"$ref":"#/components/schemas/User"}},"creator":{"$ref":"#/components/schemas/User"},"sprint":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"startAt":{"type":"string","nullable":true},"dueAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"completedAt":{"type":"string","nullable":true},"commentCount":{"type":"integer"},"attachmentCount":{"type":"integer"}}}}}}}},"400":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"429":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/tasks/{id}":{"get":{"summary":"Chi tiết một task kèm bình luận","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id task đầy đủ hoặc 8 ký tự đầu"}],"responses":{"200":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"priority":{"type":"string"},"assignees":{"type":"array","items":{"$ref":"#/components/schemas/User"}},"creator":{"$ref":"#/components/schemas/User"},"sprint":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"startAt":{"type":"string","nullable":true},"dueAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"completedAt":{"type":"string","nullable":true},"commentCount":{"type":"integer"},"attachmentCount":{"type":"integer"}}}}}}}},"404":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"patch":{"summary":"Sửa task: chỉ các trường gửi lên mới đổi (cần key write)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id task đầy đủ hoặc 8 ký tự đầu"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"title":{"type":"string","maxLength":200},"description":{"type":"string","maxLength":20000},"status":{"type":"string","enum":["backlog","todo","doing","done"],"description":"Mặc định todo khi tạo. backlog = chưa vào sprint; todo/doing/done tự vào sprint đang chạy nếu có."},"type":{"type":"string","enum":["task","bug","meeting"],"default":"task"},"priority":{"type":"string","enum":["low","medium","high"],"default":"medium"},"startAt":{"type":"string","format":"date-time","nullable":true,"description":"Ngày bắt đầu (ISO 8601 kèm múi giờ); null = xoá. Không được sau dueAt."},"dueAt":{"type":"string","format":"date-time","nullable":true,"description":"Hạn chót (ISO 8601 kèm múi giờ, VD 2026-10-05T17:00:00+07:00); null = xoá."},"assignees":{"type":"array","items":{"type":"string"},"maxItems":10,"description":"Username hoặc tên hiển thị của thành viên (xem GET /users). Khi sửa: thay cả danh sách."},"sprint":{"type":"string","nullable":true,"description":"Tên sprint, \"current\" (sprint đang chạy), hoặc id; null = không thuộc sprint (Backlog)."},"version":{"type":"string","description":"updatedAt đang thấy; lệch thì báo 409 (tránh ghi đè người khác)"}}}}}},"responses":{"200":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"priority":{"type":"string"},"assignees":{"type":"array","items":{"$ref":"#/components/schemas/User"}},"creator":{"$ref":"#/components/schemas/User"},"sprint":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"startAt":{"type":"string","nullable":true},"dueAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"completedAt":{"type":"string","nullable":true},"commentCount":{"type":"integer"},"attachmentCount":{"type":"integer"}}}}}}}},"400":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/tasks/{id}/comments":{"post":{"summary":"Viết bình luận (cần key write)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id task đầy đủ hoặc 8 ký tự đầu"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":5000}}}}}},"responses":{"201":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Lỗi","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/activity":{"get":{"summary":"Nhật ký thay đổi để agent hỏi định kỳ thay cho webhook","parameters":[{"name":"after","in":"query","schema":{"type":"integer"},"description":"Chỉ lấy sự kiện có id lớn hơn. Lưu lastId của lần trước làm after cho lần sau."},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200}}],"responses":{"200":{"description":"Thành công","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object"}},"lastId":{"type":"integer"}}}}}}}}}}}