Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.crewship.ai/llms.txt

Use this file to discover all available pages before exploring further.

Milestones

A milestone is a named checkpoint inside a project — typically a release target, a deadline, or a logical chunk of work. Each milestone tracks an issue rollup (issue_count, done_count) so the FE can render burn-down without a separate query. Implementation: internal/api/milestone_handler.go. Backed by the milestones table; issue counts are computed from missions WHERE mission_type = 'issue'. All endpoints require an authenticated session and workspace context. The URL space is split:
  • List / Create are nested under the project: /api/v1/projects/{projectId}/milestones. The project must belong to the calling workspace (projects.workspace_id), otherwise 404.
  • Update / Delete address the milestone directly: /api/v1/milestones/{milestoneId}. Workspace ownership is verified by joining milestones → projects → workspace_id.

Milestone shape

FieldTypeNotes
idstring (CUID)
project_idstringOwning project.
namestringDisplay name.
descriptionstring | nullMarkdown body.
target_datestring | nullISO 8601 date (YYYY-MM-DD) or full RFC3339 — stored verbatim.
statusstringactive, completed, archived, … (server doesn’t enforce the enum). Defaults to "active" on create when omitted.
positionintOrdering within the project, ascending. Auto-assigned on create as MAX(position) + 1.
issue_countintIssues attached to this milestone (missions WHERE milestone_id = ? AND mission_type = 'issue').
done_countintSubset of issue_count where status IN ('DONE', 'COMPLETED').
created_atRFC3339
updated_atRFC3339

GET /api/v1/projects/{projectId}/milestones

List milestones in a project, ordered by position ASC, created_at ASC. Auth: authenticated session + workspace context. Response: 200 OK — JSON array (never null).
[
  {
    "id": "mls_01HVZ...",
    "project_id": "prj_q3_release",
    "name": "Beta cut",
    "description": "Feature freeze + first external beta tag.",
    "target_date": "2026-06-15",
    "status": "active",
    "position": 1,
    "issue_count": 23,
    "done_count": 11,
    "created_at": "2026-04-01T10:00:00Z",
    "updated_at": "2026-05-12T14:30:00Z"
  }
]
The List query uses a LEFT JOIN against an aggregated subquery so milestones with zero issues still appear (counts come back as 0, not null).
StatusCondition
401Not authenticated.
404Project not found, or project does not belong to the current workspace.

POST /api/v1/projects/{projectId}/milestones

Create a new milestone in the project. Position is auto-assigned at the end of the project’s milestone list. Auth: authenticated session + workspace context + OWNER, ADMIN, or MANAGER role (requireRole("create")). Request body:
FieldTypeRequiredDefaultNotes
namestringYesEmpty → 400 name is required.
descriptionstringNonull
target_datestringNonullServer stores it verbatim — pass YYYY-MM-DD or full RFC3339; no parse-time validation.
statusstringNo"active"Empty string is normalised to "active".
Response: 201 Created with the milestone object (issue_count: 0, done_count: 0). WebSocket event: milestone.created broadcast on the workspace channel with { "id": "<id>", "project_id": "<projectId>" }.
StatusCondition
400Malformed JSON body or missing name.
401Not authenticated.
403Caller is below the MANAGER role.
404Project not found in this workspace.

PATCH /api/v1/milestones/{milestoneId}

Partial update. Workspace ownership is verified by joining milestones → projects → workspace_id — a cross-workspace id returns 404, not 403, so the surface can’t be probed for which milestone ids exist. Auth: authenticated session + workspace context + OWNER, ADMIN, or MANAGER role. Request body: every field optional.
FieldTypeNotes
namestring
descriptionstring
target_datestringSame loose format as on create.
statusstring
positionintReorder within the project.
Response: 200 OK with the full updated milestone object (rollup counts are recomputed inline via correlated subqueries). WebSocket event: milestone.updated broadcast on the workspace channel with { "id": "<id>", "project_id": "<projectId>" }.
StatusCondition
400Malformed JSON body or no fields to update.
401Not authenticated.
403Caller is below the MANAGER role.
404Milestone id not found in this workspace.

DELETE /api/v1/milestones/{milestoneId}

Hard delete the milestone. Attached issues are not deleted — they’re unlinked from the milestone (UPDATE missions SET milestone_id = NULL WHERE milestone_id = ?) inside the same transaction as the milestone row delete. This keeps history intact while letting the project be reorganised without orphan FK errors. Auth: authenticated session + workspace context + OWNER or ADMIN role (requireRole("manage")). Response: 204 No Content. WebSocket event: milestone.deleted broadcast on the workspace channel with { "id": "<id>", "project_id": "<projectId>" }.
StatusCondition
401Not authenticated.
403Caller is below the ADMIN role.
404Milestone id not found in this workspace.

See also

  • Issuesmilestone_id on a mission attaches it here; delete unlinks instead of cascading.
  • Recurring Issues — can target a milestone via milestone_id so every fire lands in the right bucket.
  • Crews — projects belong to a workspace, not a crew, but crews are the runtime container for the missions filed against the milestone.