Create workflow pins
Pin one catalog workflow to a work item type in one or more projects. For each project, Plane creates the pin or moves the project's existing pin for the type to this workflow. It also writes the project's pick to the same workflow. See Pins.
A pinned project can't change its own pick for the type. Update a work item type workflow returns 403 workflow_assigned_by_workspace until you delete the pin.
Pinning moves work items without a mapping
Pins take no state_mapping. Work items of this type whose state the pinned workflow doesn't contain move to the workflow's default state. Plane records each move on the work item. To check the count first, pass the type and the workflow to Preview a workflow fallback.
Path parameters
slug:requiredstringThe workspace slug. It appears in your Plane URLs. In https://app.plane.so/my-team/projects/, the slug is my-team.
type_id:requiredstring (uuid)The work item type to pin. It can be any type in the workspace.
Body parameters
workflow_id:requiredstring (uuid)The catalog workflow to pin. It must be active. Under constrained mode, it must also be in the type's allowlist, or the request returns 400 workflow_outside_allowlist.
project_ids:requiredarray of string (uuid)The projects to pin, at least one. Every id must be a project in this workspace. If any isn't, the request returns 400 invalid_request with the offending ids in unknown_project_ids, and Plane pins nothing.
Response shaping
fields:optionalstringComma-separated list of fields to return on each pin. Fields you don't request are left out of the response instead of coming back as null, so a missing key means you didn't ask for it and null means the value is null. id is always included, whether or not you name it.
Pass all to get every requestable field. An unknown field name returns 400 with the list of valid names and the closest match, so a typo is caught instead of being silently ignored.
Requestable here: created_at, id, project_id, updated_at, work_item_type_id, workflow_id.
See Sparse fields.
Scopes
workspaces.workflows:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | workflow_id or project_ids missing, an empty project_ids, a project outside this workspace (see unknown_project_ids), or an unknown or inactive workflow. |
400 | workflow_outside_allowlist | The type is in constrained mode and the workflow isn't on its allowlist. |
401 | unauthorized | Missing or invalid credentials. |
402 | payment_required | The Workspace governance feature isn't on your plan, or is turned off. |
403 | forbidden | Your role or token scope can't edit workspace workflows. |
404 | not_found | No such work item type in this workspace. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
409 | states_managed_at_project | The workspace doesn't have governance turned on. |
409 | workflow_pins_unavailable | The type is in required mode. The mandate already fixes the workflow for every project. |
413 | payload_too_large | The request body is over the size limit. |
415 | unsupported_media_type | The Content-Type isn't one this endpoint accepts. |
429 | rate_limited | Throttled. Honor the Retry-After header before retrying. |
curl -X POST \
"https://api.plane.so/api/v2/workspaces/my-team/work-item-types/d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240/governance/pins/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflow_id": "a1f4c7e2-3b98-4d61-8e25-6c0b9d7f3a14",
"project_ids": [
"4af68566-94a4-4eb3-94aa-50dc9427067b",
"7c19e5b3-2d84-4f6a-b0e7-93a1c5d8f246"
]
}'import requests
response = requests.post(
"https://api.plane.so/api/v2/workspaces/my-team/work-item-types/d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240/governance/pins/",
headers={"X-Api-Key": "your-api-key"},
json={
"workflow_id": "a1f4c7e2-3b98-4d61-8e25-6c0b9d7f3a14",
"project_ids": [
"4af68566-94a4-4eb3-94aa-50dc9427067b",
"7c19e5b3-2d84-4f6a-b0e7-93a1c5d8f246",
],
},
)
print(response.json())const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/work-item-types/d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240/governance/pins/",
{
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
workflow_id: "a1f4c7e2-3b98-4d61-8e25-6c0b9d7f3a14",
project_ids: ["4af68566-94a4-4eb3-94aa-50dc9427067b", "7c19e5b3-2d84-4f6a-b0e7-93a1c5d8f246"],
}),
},
);
const data = await response.json();[
{
"id": "6f2a8c41-9d37-4b05-a1e8-3c7b2d9f5e16",
"project_id": "7c19e5b3-2d84-4f6a-b0e7-93a1c5d8f246",
"workflow_id": "a1f4c7e2-3b98-4d61-8e25-6c0b9d7f3a14",
"work_item_type_id": "d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240",
"created_at": "2026-01-14T09:22:41.512907Z",
"updated_at": "2026-01-14T09:22:41.512907Z"
},
{
"id": "0b6d3f92-7e15-4a48-9c27-5f8e1a4d6b30",
"project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
"workflow_id": "a1f4c7e2-3b98-4d61-8e25-6c0b9d7f3a14",
"work_item_type_id": "d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240",
"created_at": "2026-01-14T09:22:41.478363Z",
"updated_at": "2026-01-14T09:22:41.478363Z"
}
]{
"type": "conflict",
"code": "workflow_pins_unavailable",
"detail": "Pinning is unavailable while the type is in required mode — the mandate already fixes the workflow."
}{
"type": "invalid_request",
"code": "workflow_outside_allowlist",
"detail": "That workflow is not in this type's allowlist."
}{
"type": "invalid_request",
"code": "invalid_request",
"detail": "Every project_id must belong to this workspace.",
"unknown_project_ids": ["2e8b4d17-5c93-4a60-8f1d-b7a3e6c09d52"]
}The response is a plain array
A successful call returns 201 with a JSON array of the pins for the projects you sent, newest first. The array isn't wrapped in the data envelope that List workflow pins uses.

