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.

Issues are stored in the missions table with mission_type = 'issue'. They share infrastructure with missions but have their own status flow, identifiers (e.g., ENG-42), and lifecycle operations. All issue endpoints require authentication and workspace context.

List Issues (Workspace)

GET /api/v1/issues?workspace_id={workspaceId}
Returns issues across the workspace with filtering, sorting, and pagination. Query Parameters:
ParameterTypeDefaultDescription
workspace_idstringRequiredWorkspace ID
mission_typestring"issue"Filter by type ("issue" or "mission")
statusstringComma-separated status filter (e.g., TODO,IN_PROGRESS)
prioritystringComma-separated priority filter (e.g., urgent,high)
project_idstringFilter by project
crew_idstringFilter by crew
assignee_idstringFilter by assignee
labelstringFilter by label name
searchstringSearch in title (LIKE match)
sortstring"created_at"Sort column: created_at, updated_at, priority, sort_order
limitinteger50Max items (1-100)
offsetinteger0Pagination offset
Response: 200 OK
[
  {
    "id": "issue_abc",
    "workspace_id": "ws_123",
    "crew_id": "crew_456",
    "crew_name": "Engineering",
    "crew_slug": "engineering",
    "number": 42,
    "identifier": "ENG-42",
    "title": "Fix authentication token refresh",
    "description": "Tokens are not being refreshed before expiry",
    "status": "TODO",
    "priority": "high",
    "assignee_type": "agent",
    "assignee_id": "agent_789",
    "assignee_name": "Backend Dev",
    "due_date": "2024-02-01",
    "sort_order": 0.0,
    "mission_type": "issue",
    "lead_agent_id": "agent_lead",
    "created_at": "2024-01-15T10:00:00Z",
    "updated_at": "2024-01-15T10:00:00Z",
    "completed_at": null,
    "labels": [
      {
        "id": "label_abc",
        "name": "bug",
        "color": "#ef4444",
        "label_group": null
      }
    ],
    "project_id": "proj_123",
    "project_name": "Q1 Backend Overhaul",
    "comment_count": 3
  }
]

Response Fields

FieldTypeDescription
idstringIssue ID (same as underlying mission ID)
workspace_idstringWorkspace ID
crew_idstringCrew ID
crew_namestringCrew display name
crew_slugstringCrew slug
numberintegerSequential issue number within the crew
identifierstringHuman-readable identifier (e.g., ENG-42)
titlestringIssue title
descriptionstring?Issue description
statusstringCurrent status
prioritystringPriority level
assignee_typestring?"user" or "agent"
assignee_idstring?Assignee ID
assignee_namestring?Resolved assignee name
due_datestring?Due date
sort_ordernumberManual sort order for board views
mission_typestringAlways "issue" for issues
lead_agent_idstringCrew’s lead agent ID
labelsarrayArray of label objects
project_idstring?Parent project ID
project_namestring?Project name (when project_id is set)
estimateinteger?Effort/points estimate
parent_issue_idstring?Parent issue ID for sub-issues
milestone_idstring?Associated milestone ID
sub_issues_countintegerCount of child issues with parent_issue_id = this issue
routine_idstring?Bound pipeline ID for /run-routine (omitted if unbound)
routine_slugstring?Slug of the bound pipeline (denormalized for UI)
routine_namestring?Name of the bound pipeline (denormalized for UI)
comment_countintegerNumber of comments
created_atstringISO 8601 timestamp
updated_atstringISO 8601 timestamp
completed_atstring?When issue was completed/cancelled

Get Issue (Workspace-scoped)

GET /api/v1/issues/{identifier}?workspace_id={workspaceId}
Get an issue by its identifier (e.g., ENG-42) across the entire workspace. No crew ID needed. Response: 200 OK — full issue object with labels and comment count.
StatusCondition
404Issue not found

Create Issue

POST /api/v1/crews/{crewId}/issues?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role The crew must have a LEAD agent. The issue identifier is auto-generated from the crew’s issue_prefix (or first 3 characters of the slug) and an auto-incrementing counter. Request Body:
FieldTypeRequiredDefaultDescription
titlestringYesIssue title
descriptionstringNonullIssue description
prioritystringNo"none"Priority: none, low, medium, high, urgent
assignee_typestringNonull"user" or "agent"
assignee_idstringNonullAssignee ID
due_datestringNonullDue date (YYYY-MM-DD)
project_idstringNonullParent project ID
estimateintegerNonullEffort/points estimate
parent_issue_idstringNonullParent issue ID for sub-issues (validated against current workspace)
milestone_idstringNonullAssociated milestone ID
labelsstring[]No[]Array of label IDs to attach
routine_idstringNonullPipeline ID to bind for /run-routine (must exist in workspace)
routine_inputsobjectNo{}JSON object of routine input values; rejected with 400 if set without routine_id
{
  "title": "Fix authentication token refresh",
  "description": "Tokens are not being refreshed before expiry",
  "priority": "high",
  "assignee_type": "agent",
  "assignee_id": "agent_789",
  "labels": ["label_bug"]
}
Response: 201 Created — issue object with generated number and identifier.
StatusCondition
400Missing title, crew has no lead agent, routine_inputs set without routine_id, routine_id/parent_issue_id not in workspace, or routine_inputs not valid JSON
403Insufficient role
404Crew not found
WebSocket event: issue.created on workspace:{workspaceId}

Get Issue (Crew-scoped)

GET /api/v1/crews/{crewId}/issues/{identifier}?workspace_id={workspaceId}
Get an issue by its identifier within a specific crew. Includes labels and comment count. Response: 200 OK — full issue object.

Update Issue

PATCH /api/v1/crews/{crewId}/issues/{identifier}?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role Request Body: All fields optional.
FieldTypeDescription
titlestringIssue title
descriptionstringIssue description
statusstringNew status (must be valid transition)
prioritystringPriority level
assignee_typestring"user" or "agent"
assignee_idstringAssignee ID
due_datestringDue date
project_idstringProject ID (empty string to unlink)
estimateintegerEffort/points estimate
parent_issue_idstringParent issue ID; empty string unlinks; cannot equal the issue itself
milestone_idstringMilestone ID; empty string unlinks
sort_ordernumberManual sort order
labelsstring[]Replace all labels with this set of label IDs
routine_idstringPipeline ID to bind; empty string clears binding (also resets routine_inputs)
routine_inputsobjectFull-replacement JSON object for routine inputs
Status Transitions:
FromAllowed To
BACKLOGTODO, IN_PROGRESS, CANCELLED
TODOIN_PROGRESS, BACKLOG, CANCELLED
IN_PROGRESSREVIEW, DONE, FAILED, CANCELLED, TODO
REVIEWDONE, TODO, IN_PROGRESS, FAILED, CANCELLED
DONEBACKLOG
FAILEDBACKLOG, TODO, IN_PROGRESS
CANCELLEDBACKLOG, TODO
When status changes to DONE or CANCELLED, completed_at is automatically set. Significant changes (status, assignee, priority) are logged as activity entries. Response: 200 OK — updated issue object.
StatusCondition
400Invalid status transition, no fields to update, parent_issue_id equals issue itself, parent_issue_id/routine_id not in workspace, or routine_inputs not valid JSON
404Issue not found
WebSocket event: issue.updated on workspace:{workspaceId}

Delete Issue

DELETE /api/v1/crews/{crewId}/issues/{identifier}?workspace_id={workspaceId}
Hard-deletes the issue. Only issues in BACKLOG or CANCELLED status can be deleted. Auth: OWNER, ADMIN, or MANAGER role Response: 204 No Content
StatusCondition
400Issue is not in BACKLOG or CANCELLED status
404Issue not found
WebSocket event: issue.deleted on workspace:{workspaceId}

Start Issue

POST /api/v1/crews/{crewId}/issues/{identifier}/start?workspace_id={workspaceId}
Starts execution of a BACKLOG or TODO issue. The issue must have an assignee. This:
  1. Creates a synthetic chat session for the mission
  2. Creates or resets tasks (resets existing tasks to PENDING for re-runs)
  3. Transitions status to IN_PROGRESS
  4. Starts the MissionEngine asynchronously
Auth: OWNER, ADMIN, or MANAGER role Response: 200 OK
{
  "status": "IN_PROGRESS",
  "identifier": "ENG-42"
}
StatusCondition
400Issue not in BACKLOG/TODO, or no assignee
404Issue not found
409Issue was already started by another request
WebSocket event: issue.started on workspace:{workspaceId}

Stop Issue

POST /api/v1/crews/{crewId}/issues/{identifier}/stop?workspace_id={workspaceId}
Stops a running issue. Cancels all running/pending tasks and sets status to CANCELLED. Only issues in IN_PROGRESS or REVIEW status can be stopped. Auth: OWNER, ADMIN, or MANAGER role Response: 200 OK
{
  "status": "CANCELLED",
  "identifier": "ENG-42"
}
StatusCondition
400Issue not in IN_PROGRESS or REVIEW status
404Issue not found

Review Issue

POST /api/v1/crews/{crewId}/issues/{identifier}/review?workspace_id={workspaceId}
Submit a review decision for an issue in REVIEW or IN_PROGRESS status. Auth: OWNER, ADMIN, or MANAGER role Request Body:
FieldTypeRequiredDescription
actionstringYes"approve" or "request_changes"
commentstringNoReview comment
reassign_tostringNoAgent slug to reassign to (for request_changes)
{
  "action": "request_changes",
  "comment": "The error handling needs improvement",
  "reassign_to": "backend-dev"
}
  • approve: Transitions issue to DONE, adds approval comment and activity
  • request_changes: Transitions issue to TODO, optionally reassigns, adds comment and activity
Response: 200 OK
{
  "status": "ok",
  "action": "request_changes"
}
StatusCondition
400Invalid action, or issue not in reviewable status
404Issue not found

Bulk Update

PATCH /api/v1/issues/bulk
Apply a single set of field updates to up to 100 issues at once. Used by the board/list views’ multi-select toolbar. Each issue is processed in its own statement — partial success is possible: a request touching 50 ids where 3 have invalid status transitions returns {"updated": 47} rather than rolling back. Auth: OWNER, ADMIN, or MANAGER role (create permission). Request Body:
FieldTypeRequiredDescription
idsstring[]Yes1-100 mission ids. Ids outside the caller’s workspace are skipped silently.
updates.statusstringNoNew status. Invalid transitions are skipped per-issue (not 400). When DONE or CANCELLED, completed_at is set automatically.
updates.prioritystringNoPriority level.
updates.assignee_typestringNo"user" or "agent".
updates.assignee_idstringNoAssignee id.
updates.project_idstringNoProject id; empty string unlinks.
updates.labelsstring[]NoReplace all labels on each issue with this set of label ids.
{
  "ids": ["issue_abc", "issue_def", "issue_ghi"],
  "updates": {
    "status": "TODO",
    "priority": "high",
    "labels": ["label_bug", "label_blocker"]
  }
}
Response: 200 OK
{ "updated": 3 }
FieldTypeDescription
updatedintegerCount of issues that successfully applied at least one field. Issues whose updates resolved to a no-op are excluded.
StatusCondition
400Invalid JSON, missing ids, or ids length > 100.
403Caller lacks the create permission for the workspace.
When updated > 0, an issues.bulk_updated WebSocket event is broadcast on workspace:{workspaceId} with the count. Per-issue status changes additionally produce activity entries (status_changed, details suffixed with (bulk)).

List Sub-issues

GET /api/v1/crews/{crewId}/issues/{identifier}/subtasks
Returns the children of one parent issue — rows where parent_issue_id equals the resolved parent id. Ordered by sort_order ASC, created_at ASC so the response matches the order operators see in the UI’s nested list. Path parameters:
ParamDescription
crewIdCrew id (used to disambiguate identifiers across crews).
identifierHuman-readable identifier (e.g. ENG-42) or raw mission id of the parent.
Response: 200 OK — array of full issue objects matching the GET /api/v1/issues shape. Empty array (never null) when the parent has no sub-issues.
StatusCondition
404Parent issue not found in the supplied crew + workspace.
500DB read failure.

List Activity

GET /api/v1/crews/{crewId}/issues/{identifier}/activity?workspace_id={workspaceId}
Returns the activity log for an issue (status changes, assignments, reviews). Limited to 50 most recent entries. Response: 200 OK
[
  {
    "id": "act_abc",
    "mission_id": "issue_123",
    "actor_type": "user",
    "actor_id": "user_456",
    "actor_name": "John Doe",
    "action": "status_changed",
    "details": "TODO -> IN_PROGRESS",
    "created_at": "2024-01-15T10:00:00Z"
  }
]
Activity Actions: status_changed, assignee_changed, priority_changed, review_approved, review_changes_requested

Comments

List Comments

GET /api/v1/crews/{crewId}/issues/{identifier}/comments?workspace_id={workspaceId}
Returns all comments on an issue, ordered by creation date ascending. Response: 200 OK
[
  {
    "id": "comment_abc",
    "mission_id": "issue_123",
    "author_type": "user",
    "author_id": "user_456",
    "author_name": "John Doe",
    "body": "This looks good, just fix the edge case",
    "created_at": "2024-01-15T10:00:00Z",
    "updated_at": "2024-01-15T10:00:00Z"
  }
]

Create Comment

POST /api/v1/crews/{crewId}/issues/{identifier}/comments?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role Request Body:
FieldTypeRequiredDescription
bodystringYesComment text
{
  "body": "This looks good, just fix the edge case"
}
Response: 201 Created — comment object with author_type: "user".

Relations

List Relations

GET /api/v1/crews/{crewId}/issues/{identifier}/relations?workspace_id={workspaceId}
Returns all relations for an issue. Relation types are flipped when viewed from the target side (e.g., blocks becomes blocked_by). Response: 200 OK
[
  {
    "id": "rel_abc",
    "source_id": "issue_123",
    "target_id": "issue_456",
    "relation_type": "blocks",
    "target_identifier": "ENG-43",
    "target_title": "Deploy auth service",
    "target_status": "BACKLOG",
    "created_at": "2024-01-15T10:00:00Z"
  }
]

Create Relation

POST /api/v1/crews/{crewId}/issues/{identifier}/relations?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role Request Body:
FieldTypeRequiredDescription
target_identifierstringYesTarget issue identifier (e.g., ENG-43)
relation_typestringYesblocks, blocked_by, relates_to, or duplicate_of
{
  "target_identifier": "ENG-43",
  "relation_type": "blocks"
}
Internally, blocked_by is stored as blocks with source/target swapped for normalization. Response: 201 Created
StatusCondition
400Invalid relation_type, self-relation
404Source or target issue not found
409Relation already exists

Delete Relation

DELETE /api/v1/relations/{relationId}?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role Response: 200 OK with {"status": "ok"}
StatusCondition
404Relation not found

Labels

Labels are workspace-scoped and can be attached to any issue.

List Labels

GET /api/v1/labels?workspace_id={workspaceId}
Response: 200 OK
[
  {
    "id": "label_abc",
    "name": "bug",
    "color": "#ef4444",
    "label_group": null
  }
]

Create Label

POST /api/v1/labels?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role
FieldTypeRequiredDescription
namestringYesLabel name
colorstringYesColor hex code
label_groupstringNoGrouping category
Response: 201 Created

Update Label

PATCH /api/v1/labels/{labelId}?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role All fields optional: name, color, label_group. Response: 200 OK — updated label.
StatusCondition
400No fields to update
404Label not found

Delete Label

DELETE /api/v1/labels/{labelId}?workspace_id={workspaceId}
Auth: OWNER or ADMIN role Response: 204 No Content
StatusCondition
404Label not found

Projects

Projects group related issues together with tracking metadata.

List Projects

GET /api/v1/projects?workspace_id={workspaceId}
Query Parameters:
ParameterTypeDescription
statusstringComma-separated status filter
sortstringSort column: name (default), created_at, updated_at
Response: 200 OK
[
  {
    "id": "proj_abc",
    "workspace_id": "ws_123",
    "name": "Q1 Backend Overhaul",
    "slug": "q1-backend-overhaul",
    "description": "Major backend refactoring",
    "icon": "rocket",
    "color": "blue",
    "status": "started",
    "priority": "high",
    "health": "on_track",
    "lead_type": "user",
    "lead_id": "user_123",
    "lead_name": "John Doe",
    "start_date": "2024-01-01",
    "target_date": "2024-03-31",
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-15T10:00:00Z",
    "issue_count": 12,
    "done_count": 8,
    "progress": 66
  }
]

Create Project

POST /api/v1/projects?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role
FieldTypeRequiredDefaultDescription
namestringYesProject name
descriptionstringNonullDescription
iconstringNonullLucide icon name
colorstringNo"blue"Palette color
statusstringNo"backlog"Project status
prioritystringNo"none"Priority level
lead_typestringNonull"user" or "agent"
lead_idstringNonullLead person/agent ID
start_datestringNonullStart date
target_datestringNonullTarget date
Response: 201 Created

Get Project

GET /api/v1/projects/{projectId}?workspace_id={workspaceId}
Response: 200 OK — project with computed issue_count, done_count, and progress fields.

Update Project

PATCH /api/v1/projects/{projectId}?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role All fields optional: name, description, icon, color, status, priority, health, lead_type, lead_id, start_date, target_date. Response: 200 OK — updated project.

Delete Project

DELETE /api/v1/projects/{projectId}?workspace_id={workspaceId}
Auth: OWNER or ADMIN role Unlinks all issues from the project before deletion. Response: 204 No Content

Project Stats

GET /api/v1/projects/{projectId}/stats?workspace_id={workspaceId}
Returns breakdown data: counts by status, by assignee, by label, and crew list. Response: 200 OK
{
  "total_issues": 12,
  "completed_issues": 8,
  "by_status": {
    "BACKLOG": 1,
    "TODO": 2,
    "IN_PROGRESS": 1,
    "DONE": 8
  },
  "by_assignee": [
    {
      "agent_id": "agent_abc",
      "agent_name": "Backend Dev",
      "total": 5,
      "completed": 4
    }
  ],
  "by_label": [
    {
      "label_name": "bug",
      "color": "#ef4444",
      "count": 3
    }
  ],
  "crews": ["crew_abc"]
}

Bulk Update

PATCH /api/v1/issues/bulk?workspace_id={workspaceId}
Apply the same field updates to up to 100 issues in a single request. Issues not in the caller’s workspace are silently skipped; invalid status transitions are also skipped per-issue. Auth: OWNER, ADMIN, or MANAGER role Request Body:
FieldTypeRequiredDescription
idsstring[]YesIssue IDs (1-100)
updates.statusstringNoNew status (skipped for issues where transition is invalid)
updates.prioritystringNoPriority level
updates.assignee_typestringNo"user" or "agent"
updates.assignee_idstringNoAssignee ID
updates.project_idstringNoProject ID (empty string to unlink)
updates.labelsstring[]NoReplace all labels with this set of label IDs
Response: 200 OK
{ "updated": 7 }
StatusCondition
400ids empty or > 100
WebSocket event: issues.bulk_updated on workspace:{workspaceId} (only when at least one issue was updated)

List Sub-Issues

GET /api/v1/crews/{crewId}/issues/{identifier}/subtasks?workspace_id={workspaceId}
Returns all child issues whose parent_issue_id references this issue, ordered by sort_order then created_at. Response: 200 OK — array of issue objects (same shape as List Issues).
StatusCondition
404Parent issue not found

Issue Statuses

StatusDescription
BACKLOGNot yet prioritized
TODOPlanned for work
IN_PROGRESSAgent is actively working
REVIEWWaiting for human review
DONECompleted and approved
FAILEDAgent encountered unresolvable error
CANCELLEDCancelled by user
DUPLICATEMarked as duplicate (no transitions out)

Priority Levels

ValueDescription
noneNo priority set
lowLow priority
mediumMedium priority
highHigh priority
urgentUrgent priority