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. Unrequested keys are omitted from the response, not returned as null, so absent means "not requested" and null means "actually null". id always comes back whether or not you name it.
Pass all for every requestable field. An unknown name is a 400 that lists the valid set and suggests the closest match, so a typo can't silently cost you the saving.
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 | Workspace governance isn't on your plan. |
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": "e6b2d9f1-48a7-4c3e-9f50-1d7a8c2b6e93",
"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": "e6b2d9f1-48a7-4c3e-9f50-1d7a8c2b6e93",
"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: "e6b2d9f1-48a7-4c3e-9f50-1d7a8c2b6e93",
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": "e6b2d9f1-48a7-4c3e-9f50-1d7a8c2b6e93",
"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": "e6b2d9f1-48a7-4c3e-9f50-1d7a8c2b6e93",
"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.

