7.9 KiB
7.9 KiB
04 — API Surface
Conventions
- Base path
/api/v1; JSON in/out; UTF-8 - Auth: session cookie (scs) or
Authorization: Bearer solopm_…PAT — same middleware resolves both (01-architecture.md) - Pagination:
?page=1&per_page=50(max 100); list responses{items: [...], page, per_page, total} - Filtering: documented per endpoint below; multiple values comma-separated (
status=backlog,planned) - Sorting:
?sort=field/?sort=-field(descending) - Errors:
{"error": {"code", "message", "fields?"}}; non-member project access → 404 - Issues are addressed by
project_issue_id({num}) within their project, never by global id, in all/projects/{id}/issues/{num}routes - Mutating endpoints emit domain events per 03-domain-events.md
Auth & account
POST /api/v1/auth/register— email, username, name, password; first user becomes adminPOST /api/v1/auth/login— email + password → session cookiePOST /api/v1/auth/logout— destroy sessionGET /api/v1/auth/oauth/{provider}— redirect to GitHub/Gitea authorize (provider ∈ github|gitea)GET /api/v1/auth/oauth/{provider}/callback— code exchange → upsert oauth_accounts → session cookie → redirect to SPAGET /api/v1/me— current user + memberships summary + unread notification countPATCH /api/v1/me— name, avatar_url, email_modePUT /api/v1/me/password— requires current passwordGET /api/v1/me/tokens— list PATs (prefix, name, last_used_at; never the token)POST /api/v1/me/tokens— create PAT; plaintext token returned exactly onceDELETE /api/v1/me/tokens/{id}— revokeGET /api/v1/users— pickable users for member/assignee selectors (excludes system, zombie, deleted)
Admin (RequireAdmin)
GET /api/v1/admin/users— all users incl. soft-deletedPATCH /api/v1/admin/users/{id}— toggle is_adminDELETE /api/v1/admin/users/{id}— soft-delete user (blocked withconflictif sole owner of any project — transfer first)POST /api/v1/admin/cull?dry_run=true|false— run retention job; dry-run returns would-delete report (12-retention.md)POST /api/v1/admin/search/reindex— full Meilisearch rebuildGET /api/v1/admin/jobs?status=dead— inspect job queue
Projects
GET /api/v1/projects— my projects, favorites first then recent activity; filters:status,qPOST /api/v1/projects— creates project + owner membership + seeded labelsGET /api/v1/projects/{id}— detail incl. members, labels, dependencies, my-favorite flagPATCH /api/v1/projects/{id}— fields incl. status (sets/clears completed_at)DELETE /api/v1/projects/{id}— soft delete (owner only)PUT /api/v1/projects/{id}/owner— transfer ownership to another member (owner only)PUT /api/v1/projects/{id}/favorite/DELETE …/favoriteGET /api/v1/projects/{id}/members/POST(owner only; body: user_id, role) /PATCH …/members/{userId}(role) /DELETE …/members/{userId}(owner only; owner cannot remove self without transfer)GET /api/v1/projects/{id}/dependencies/POST(409 on cycle) /DELETE …/dependencies/{depId}GET /api/v1/projects/{id}/labels/POST/PATCH …/labels/{labelId}/DELETE …/labels/{labelId}(soft)GET /api/v1/projects/{id}/activity— paged audit feed for the projectGET /api/v1/dashboard/activity— cross-project feed over my projectsGET /api/v1/projects/{id}/export?format=json|csv— streaming download (11-integrations.md)
Epics
GET /api/v1/projects/{id}/epics— list with progress (issue counts by status + story-point totals); filterstatusPOST /api/v1/projects/{id}/epicsGET /api/v1/projects/{id}/epics/{epicId}— detail incl. ordered issuesPATCH /api/v1/projects/{id}/epics/{epicId}/DELETE(soft; issues keep existing but epic_id set NULL at cull time, not delete time)PUT /api/v1/projects/{id}/epics/{epicId}/issues/{num}— attach/move issue into epic (body:after_rankfor position)DELETE /api/v1/projects/{id}/epics/{epicId}/issues/{num}— detach
Issues
GET /api/v1/projects/{id}/issues— filters:status,assignee,label,priority,epic,parent,q(title substring); sort: any column;?view=boardreturns grouped-by-status arrays ordered by board_rankPOST /api/v1/projects/{id}/issues— mintsproject_issue_id; optional epic_id, parent, assignee, labelsGET /api/v1/projects/{id}/issues/{num}— full detail (labels, watchers, blockers, sub-issues, epic, attachment/link lists)PATCH /api/v1/projects/{id}/issues/{num}— any field; status→done sets completed_at; board moves send{status, after_rank}DELETE /api/v1/projects/{id}/issues/{num}— soft deleteGET /api/v1/projects/{id}/issues/{num}/activity— audit entries ∪ comments, ascendingGET /api/v1/projects/{id}/issues/{num}/blockers/POST(409 on cycle) /DELETE …/blockers/{blockerId}PUT /api/v1/projects/{id}/issues/{num}/labels/{labelId}/DELETEPUT /api/v1/projects/{id}/issues/{num}/watch— watch (clears muted) /DELETE— unwatch (sets muted)GET /api/v1/projects/{id}/issues/{num}/comments/POSTPATCH /api/v1/comments/{commentId}(author only) /DELETE(soft; author or project owner)PUT /api/v1/comments/{commentId}/resolve/DELETE …/resolve
Links & attachments (project | issue | comment)
POST /api/v1/projects/{id}/links·POST /api/v1/projects/{id}/issues/{num}/links·POST /api/v1/comments/{commentId}/linksDELETE /api/v1/links/{linkId}(membership checked via owning resource)POST /api/v1/projects/{id}/attachments·POST /api/v1/projects/{id}/issues/{num}/attachments·POST /api/v1/comments/{commentId}/attachments— multipart, size ≤MAX_UPLOAD_MBDELETE /api/v1/attachments/{attachmentId}(soft; file removed at cull)GET /files/{path}— auth-checked attachment serving (verify requester is member of the owning project; no directory listing;Content-Dispositionfrom original_filename)
Wiki
GET /api/v1/projects/{id}/wiki— page list (title, slug, updated_by, updated_at)POST /api/v1/projects/{id}/wiki— create (slug from title; 409 on duplicate)GET /api/v1/projects/{id}/wiki/{slug}/PATCH/DELETE(soft)
Notifications & real-time
GET /api/v1/notifications?unread=true— paged inboxGET /api/v1/notifications/unread_countPUT /api/v1/notifications/{id}/read/PUT /api/v1/notifications/read_allGET /api/v1/stream— SSE; framesnotification,data,ping(09-notifications-realtime.md)
Search
GET /api/v1/search?q=&types=issue,epic,project,comment,wiki&project_id=— Meilisearch proxy, results filtered to caller's memberships (10-search.md); 503 when Meilisearch unconfigured
Webhooks (11-integrations.md)
POST /webhooks/incoming/{provider}— unauthenticated endpoint, HMAC-SHA256 verified, writes receipt, processes (outside/api/v1)GET /api/v1/projects/{id}/webhook_receipts— paged receiptsPOST /api/v1/projects/{id}/webhook_receipts/{rid}/replayGET /api/v1/projects/{id}/webhooks/POST/PATCH …/webhooks/{whId}/DELETE(all owner only)POST /api/v1/projects/{id}/webhooks/{whId}/test— send a signed test payload nowGET /api/v1/projects/{id}/webhooks/{whId}/deliveries— delivery log
Ops
GET /healthz— 200 with{db: ok, meilisearch: ok|absent, smtp: configured|absent}- All non-
/api, non-/webhooks, non-/filesGETs → embedded SPAindex.html