Skip to content

Workspace workflows overview ​

In a workspace with governance turned on, the workspace owns states and workflows instead of each project. It keeps a catalog of both:

  • Catalog states are workspace-level states that belong to no project. You manage them with workspace states.
  • Catalog workflows arrange catalog states into a chain and define the moves between them. They are the subject of this page.

Projects don't copy a catalog workflow. They resolve to one, so a change to a catalog workflow applies to every project that uses it.

A catalog workflow is made of four parts, each with its own endpoints:

How work items resolve to a workflow ​

A pick binds one project and one work item type to a catalog workflow. For every project and work item type, Plane resolves one effective workflow, in this order:

  1. The workflow the work item type mandates, when the type's governance mode is required.
  2. The workflow the workspace pinned for this project and type.
  3. The project's own pick for the type.
  4. Otherwise, the workspace default workflow, the catalog workflow with is_default: true.

Work items without a type use the project's pick for typeless work items, or the workspace default. You set governance modes, allowlists, and pins per work item type with work item type governance, and a project's picks with work item type workflows. Get workspace workflow usage shows which projects and types resolve to a workflow today.

Ungoverned workspaces ​

Every catalog endpoint, including the states, transitions, activities, and hooks under a workflow, returns 409 states_managed_at_project in an ungoverned workspace. There, each project owns its workflows. Use the project workflows instead.

The permission check runs before the governance check, so a caller without access gets 403, not 409, and learns nothing about how the workspace is configured.

Learn more about workspace governance

Learn more about workflows and approvals

The workspace workflow object ​

Attributes ​

  • id string (uuid)

    Unique identifier for the catalog workflow.

  • name string

    Display name, unique among the workspace's catalog workflows. Maximum 255 characters.

  • description string

    Free-form description. Nullable.

  • is_active boolean

    Whether the workflow's rules are enforced. A new workflow starts inactive, and activation needs at least one state in the chain. Work items that resolve to the workflow use its states whether or not it is active.

  • is_default boolean

    Whether this is the workspace default workflow, the one a project and work item type fall back to when nothing else applies. One catalog workflow is the default. Read-only.

  • states_count integer

    Number of states in the chain.

  • projects_count integer

    Number of distinct projects with a pick that points at the workflow.

  • work_item_types_count integer

    Number of distinct work item types with a pick that points at the workflow.

  • states array of object

    The chain, ordered by sequence. Detail only. Each entry has:

    • id: the catalog state id. The workspace workflow state object calls this state_id.
    • allow_issue_creation, is_default, sequence, type: the chain settings for the state.
    • transitions: the transitions that start from the state. Each has id, transition_state_id, rejection_state_id, member_ids, required_approvals, pre_rules, and post_rules.

    pre_rules and post_rules list the transition's enabled hooks in run order, each with id, handler_name, rule_type (validation for pre, action for post), and config. A send_webhook rule shows a masked config.secret.

  • referenced_resources object

    Lookup maps for every id the chain names. Detail only. states maps each catalog state id (chain members, transition targets, and rejection targets) to its id, name, color, group, sequence, default, project_id, and workspace_id. users maps each approver id to id, display_name, avatar_url, is_bot, first_name, last_name, and workspace_id.

  • project_ids array of string

    Projects with a pick that points at the workflow. Detail only.

  • work_item_type_ids array of string

    Work item types with a pick that points at the workflow. Detail only.

  • created_at string (date-time)

    When the workflow was created.

  • updated_at string (date-time)

    When the workflow was last updated.

  • created_by_id string (uuid)

    The user who created the workflow.

  • updated_by_id string (uuid)

    The user who last updated the workflow.

The list carries counts and the detail carries the chain

List rows omit states, referenced_resources, project_ids, and work_item_type_ids. Those four take extra queries to build, so only Get a workspace workflow and the write responses return them.

Response200
json
{
  "id": "9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88",
  "name": "Delivery",
  "description": "Engineering delivery flow with a review gate",
  "is_active": true,
  "is_default": false,
  "states_count": 4,
  "projects_count": 2,
  "work_item_types_count": 1,
  "states": [
    {
      "id": "2c4d16f8-9b3e-4a52-8d71-1f0e6c9a5b48",
      "allow_issue_creation": true,
      "is_default": true,
      "sequence": 10000.0,
      "type": "transition",
      "transitions": [
        {
          "id": "8d2f6a41-5c3e-4b97-a1d0-7e9c4b2f8a63",
          "transition_state_id": "f960d3c2-8524-4a41-b8eb-055ce4be2a7f",
          "rejection_state_id": null,
          "member_ids": [],
          "required_approvals": null,
          "pre_rules": [],
          "post_rules": []
        }
      ]
    },
    {
      "id": "f960d3c2-8524-4a41-b8eb-055ce4be2a7f",
      "allow_issue_creation": true,
      "is_default": false,
      "sequence": 20000.0,
      "type": "transition",
      "transitions": [
        {
          "id": "d7a3f5c9-0b2e-4d81-b6f4-9e1c3a5d7b20",
          "transition_state_id": "b7e42a19-3c5d-4f80-9a26-8d1c0f4e7b53",
          "rejection_state_id": null,
          "member_ids": [],
          "required_approvals": null,
          "pre_rules": [
            {
              "id": "9a1c5e37-8b2d-4f60-a4e9-6c3b7d0f1e28",
              "handler_name": "run_script",
              "rule_type": "validation",
              "config": {
                "script_id": "5d8e2a6c-3f1b-4a97-8c04-2e9b7d1f6a35",
                "execution_variables": {
                  "min_estimate": "1"
                }
              }
            }
          ],
          "post_rules": [
            {
              "id": "e4b7c9a2-3d1f-4e68-8a52-9f0c6b3d7e15",
              "handler_name": "send_webhook",
              "rule_type": "action",
              "config": {
                "url": "https://hooks.example.com/plane",
                "headers": [
                  {
                    "key": "X-Team",
                    "is_secret": false,
                    "value": "platform"
                  }
                ],
                "secret": "plane_wh_••••7c1d"
              }
            }
          ]
        }
      ]
    },
    {
      "id": "b7e42a19-3c5d-4f80-9a26-8d1c0f4e7b53",
      "allow_issue_creation": false,
      "is_default": false,
      "sequence": 30000.0,
      "type": "approval",
      "transitions": [
        {
          "id": "4b6e1d93-2a7f-4c58-9e30-1d8b5f7a2c46",
          "transition_state_id": "7b1e9d40-3c86-4f2a-9a5d-8e2b0c47d613",
          "rejection_state_id": "f960d3c2-8524-4a41-b8eb-055ce4be2a7f",
          "member_ids": ["a3f92c71-6e4b-4d08-9b5a-1c7e3d9f2b84"],
          "required_approvals": 1,
          "pre_rules": [],
          "post_rules": []
        }
      ]
    },
    {
      "id": "7b1e9d40-3c86-4f2a-9a5d-8e2b0c47d613",
      "allow_issue_creation": false,
      "is_default": false,
      "sequence": 40000.0,
      "type": "transition",
      "transitions": []
    }
  ],
  "referenced_resources": {
    "states": {
      "2c4d16f8-9b3e-4a52-8d71-1f0e6c9a5b48": {
        "id": "2c4d16f8-9b3e-4a52-8d71-1f0e6c9a5b48",
        "name": "Backlog",
        "color": "#8b8d98",
        "group": "backlog",
        "sequence": 15000.0,
        "default": false,
        "project_id": null,
        "workspace_id": "b1c4e7a2-5d8f-4a3b-9c6e-0f2d4a7b8c91"
      },
      "f960d3c2-8524-4a41-b8eb-055ce4be2a7f": {
        "id": "f960d3c2-8524-4a41-b8eb-055ce4be2a7f",
        "name": "In Progress",
        "color": "#3f76ff",
        "group": "started",
        "sequence": 25000.0,
        "default": false,
        "project_id": null,
        "workspace_id": "b1c4e7a2-5d8f-4a3b-9c6e-0f2d4a7b8c91"
      },
      "b7e42a19-3c5d-4f80-9a26-8d1c0f4e7b53": {
        "id": "b7e42a19-3c5d-4f80-9a26-8d1c0f4e7b53",
        "name": "In Review",
        "color": "#f5a623",
        "group": "started",
        "sequence": 35000.0,
        "default": false,
        "project_id": null,
        "workspace_id": "b1c4e7a2-5d8f-4a3b-9c6e-0f2d4a7b8c91"
      },
      "7b1e9d40-3c86-4f2a-9a5d-8e2b0c47d613": {
        "id": "7b1e9d40-3c86-4f2a-9a5d-8e2b0c47d613",
        "name": "Done",
        "color": "#26a05f",
        "group": "completed",
        "sequence": 45000.0,
        "default": false,
        "project_id": null,
        "workspace_id": "b1c4e7a2-5d8f-4a3b-9c6e-0f2d4a7b8c91"
      }
    },
    "users": {
      "a3f92c71-6e4b-4d08-9b5a-1c7e3d9f2b84": {
        "id": "a3f92c71-6e4b-4d08-9b5a-1c7e3d9f2b84",
        "display_name": "priya",
        "avatar_url": null,
        "is_bot": false,
        "first_name": "Priya",
        "last_name": "Raman",
        "workspace_id": "b1c4e7a2-5d8f-4a3b-9c6e-0f2d4a7b8c91"
      }
    }
  },
  "project_ids": ["4af68566-94a4-4eb3-94aa-50dc9427067b", "d1e8a3c5-7f2b-4a96-8e04-3b6c9d1f5a27"],
  "work_item_type_ids": ["2d9d1a97-5c6f-4a1e-9d5b-8c2f7e30b6a4"],
  "created_at": "2026-01-14T09:22:41.478363Z",
  "updated_at": "2026-01-15T11:04:52.660219Z",
  "created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
  "updated_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
}

Build a workflow ​

  1. Create the workflow with Create a workspace workflow. It starts as an inactive draft with an empty chain.
  2. Attach catalog states with Add states to a workspace workflow.
  3. Choose where new work items land with Mark a workspace workflow state as default.
  4. Add the allowed moves with Create a workspace workflow transition, and hooks on them with Create a workspace workflow hook.
  5. Activate the workflow by sending {"is_active": true} to Update a workspace workflow.

To set the whole chain, its order, and its default in one request, send states and default_state_id to Update a workspace workflow in place of steps 2 and 3.

Plan features ​

Some operations need a plan feature. When the feature is off, they return 402 payment_required.

OperationPlan feature
List and get workspace workflows, and get usageNone
Create and update a workspace workflow, and every state, transition, activity, and hook endpoint under oneWorkflows
Create a second or later catalog workflowMultiple workflows
Delete a workspace workflowMultiple workflows
Set a chain state's type to approval, or create or update a transition that starts from an approval stateWorkflow approvals
Create or update a run_script hookPlane Runner
Create or update a send_webhook hookWebhook transition conditions

Guards on removing states and deleting workflows ​

Three 409 codes protect work items and the records that reference a workflow:

  • workflow_state_orphans: removing states from a chain would leave work items in a state outside it. Plane counts the work items in every project and work item type whose pick points at the workflow. The count includes archived work items and excludes drafts and work items in triage. Send a state_mapping from each stranded state to a state that stays in the chain. The body's orphans member lists the stranded states with counts per project. Update a workspace workflow and Remove a state from a workspace workflow return it.
  • workflow_state_dependencies: a transition still starts from, targets, or rejects to the state you're removing. Delete those transitions first. Remove a state from a workspace workflow returns it.
  • workflow_in_use: the workflow is the workspace default, or a project pick, a pin, or a type allowlist references it. Delete a workspace workflow returns it.

Endpoints ​

MethodPathDescription
GET/api/v2/workspaces/{slug}/workflows/List workspace workflows
GET/api/v2/workspaces/{slug}/workflows/{pk}/Get a workspace workflow
POST/api/v2/workspaces/{slug}/workflows/Create a workspace workflow
PATCH/api/v2/workspaces/{slug}/workflows/{pk}/Update a workspace workflow
DELETE/api/v2/workspaces/{slug}/workflows/{pk}/Delete a workspace workflow
GET/api/v2/workspaces/{slug}/workflows/{pk}/usage/Get workspace workflow usage

Response shaping ​

Every endpoint that returns the workspace workflow object accepts ?fields= for sparse responses. See Sparse fields. On the list, naming a detail-only field (states, referenced_resources, project_ids, or work_item_type_ids) is a 400. Errors follow the shared problem+json contract.

Changed from v1 ​

  • In an ungoverned workspace, the catalog returns 409 states_managed_at_project. v1 returns 400 with its own error body.
  • Deleting a workflow that is in use returns 409 workflow_in_use. v1 returns 400.
  • The orphan report is a problem+json body with the code workflow_state_orphans, and the report sits under orphans. v1 returns the bare report.
  • List rows add description, created_at, updated_at, created_by_id, and updated_by_id, and drop workspace_id and project_id. The detail reports created_by_id and updated_by_id in place of created_by and updated_by.
  • Chain states have read routes, and the transition and activity lists are paginated.

See Migrating from v1 for the full list.