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.

Workflow Templates

A workflow template defines an ordered list of stages (e.g. backlog → in_progress → review → done) that crews attach to their issue trackers. The frontend renders these as column headers on board views; the issue handler enforces the stage transitions when an issue moves between columns. Implementation: internal/api/workflow_templates_handler.go. Backed by the workflow_templates table (schema introduced in migration v19, see internal/database/migrate_consts_v16_v25.go). All endpoints require an authenticated session and workspace context. Mutating verbs additionally require OWNER, ADMIN, or MANAGER role (requireRole("create")).

Template shape

FieldTypeNotes
idstring (CUID)
namestringWorkspace-unique. The DB has a UNIQUE(workspace_id, name) index — collisions on POST/PATCH surface as 409.
descriptionstring | null
template_jsonstringJSON-encoded stage array. See Stage shape. Stored canonicalised — the server re-marshals it on every write so byte-identical manifests round-trip cleanly.
iconstring | nullEmoji shortcode or single Unicode glyph the frontend renders next to the template name.
colorstring | nullHex string like #3B82F6. Validated against `^#([0-9a-fA-F][0-9a-fA-F])$`.
is_builtinboolServer-controlled. true for the seed-time templates; user-created rows are always false. Callers cannot mark their own rows as built-in.
created_atRFC3339
updated_atRFC3339

Stage shape

Stages live inside template_json as a JSON array. Each stage:
FieldTypeRequiredNotes
namestringYesUnique within the template (case-sensitive — the DB stores it verbatim).
typestringYesOne of open, started, completed, cancelled. Drives behaviour beyond display (e.g. closed-state inference).
positionintegerYesPositive integer; unique within the template; controls left-to-right order on board views. 0 and negative values reject as 400.
colorstringNoHex string like #3B82F6 (3- or 6-digit). Empty/missing renders with the workspace default.
Validation rules enforced by validateTemplateJSON:
  • At least one stage.
  • Exactly one stage with type: open.
  • At least one stage with type: completed.
  • All name values unique; all position values unique.
Violations return 400 with a single-sentence message describing the failing constraint.

GET /api/v1/workflow-templates

List every template visible to the current workspace. Built-in rows surface first (is_builtin DESC), then user-created rows by created_at ASC. Auth: authenticated session + workspace context. Response: 200 OK — JSON array (never null).
[
  {
    "id": "wt_01HVZ...",
    "name": "Engineering Standard",
    "description": "Default engineering lifecycle",
    "template_json": "[{\"name\":\"backlog\",\"type\":\"open\",\"position\":1,\"color\":\"#9CA3AF\"},{\"name\":\"in_progress\",\"type\":\"started\",\"position\":2,\"color\":\"#3B82F6\"},{\"name\":\"done\",\"type\":\"completed\",\"position\":3,\"color\":\"#10B981\"}]",
    "icon": ":hammer_and_wrench:",
    "color": "#3B82F6",
    "is_builtin": false,
    "created_at": "2026-05-19T18:22:10Z",
    "updated_at": "2026-05-19T18:22:10Z"
  }
]

POST /api/v1/workflow-templates

Create a new user-owned template. Auth: authenticated session + workspace context + requireRole("create"). Request body:
FieldTypeRequiredNotes
namestringYesWorkspace-unique.
descriptionstring | nullNo
template_jsonstringYesStage array (see Stage shape). The handler canonicalises this — what the GET reads back is byte-equal regardless of the whitespace you posted.
iconstring | nullNo
colorstring | nullNoHex string.
Response: 201 Created with the full template object.
StatusCondition
400Missing name, missing/invalid template_json (any stage rule above), or invalid color.
401Not authenticated.
403Caller is below the MANAGER role.
409A template with the same name already exists in this workspace.
Broadcasts workflow_template.created on the workspace WS channel with {"id": "<id>"}.

GET /api/v1/workflow-templates/{id}

Fetch a single template by id, scoped to the calling workspace. Auth: authenticated session + workspace context. Response: 200 OK with the template object.
StatusCondition
404Template id does not exist, or exists in a different workspace. The handler deliberately collapses these into one response code so cross-workspace existence can’t be inferred.

PATCH /api/v1/workflow-templates/{id}

Partial update. Only the mutable subset (name, description, template_json, icon, color) is writable. is_builtin, created_at, and id are immutable; updated_at is set to “now” on every successful write. Auth: authenticated session + workspace context + requireRole("create"). Request body: every field optional; only provided (non-null) keys are written.
FieldTypeNotes
namestringEmpty string → 400 name cannot be empty.
descriptionstringEmpty string clears the column (stored as SQL NULL).
template_jsonstringRe-validated against the full stage rule set on every write — there is no partial-stage edit path.
iconstringEmpty string clears to NULL.
colorstringEmpty string clears to NULL; non-empty must match the hex regex.
Response: 200 OK with the freshly-updated template (so callers don’t need to re-GET).
StatusCondition
400Malformed body, no fields supplied, or any stage rule violation.
401Not authenticated.
403Caller is below the MANAGER role.
404Template id does not exist in this workspace.
409New name collides with an existing template in the workspace.
Broadcasts workflow_template.updated on the workspace WS channel with {"id": "<id>"}.

DELETE /api/v1/workflow-templates/{id}

Hard delete. Templates are reference data — crews that still point at the deleted template fall back to the runtime default lifecycle at next render rather than failing. Auth: authenticated session + workspace context + requireRole("create"). Response: 204 No Content.
StatusCondition
401Not authenticated.
403Caller is below the MANAGER role.
404Template id does not exist in this workspace.
Broadcasts workflow_template.deleted on the workspace WS channel with {"id": "<id>"}.

See also