Update a workspace workflow hook
Turn a hook on or off, or change its settings. phase and handler_name are fixed at creation. Sending either one returns 400, even with the current value. To change them, delete the hook and create a new one.
PATCH is partial. Plane checks the plan feature for the hook's handler again on every update, so you can't edit a hook whose feature has left your plan.
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.
workflow_id:requiredstring (uuid)The catalog workflow the transition belongs to.
transition_id:requiredstring (uuid)The transition the hooks run on. It must belong to the workflow in the path, or the request returns 404.
pk:requiredstring (uuid)The id of the hook.
Body Parameters
execution_order isn't writable.
is_enabled:optionalbooleanWhether the hook runs. A disabled hook stays listed but doesn't run.
config:optionalobjectNew handler settings, in the same shape as on create. What you send merges with what is stored:
run_script: the keys you send replace the stored ones, and keys you leave out keep their values, so aconfigwithoutscript_idkeeps the current script.send_webhook: an omittedurlkeeps the stored URL, and an omittedheaderskeeps the stored headers. Aheadersarray replaces the stored list. In it, a secret header sent withoutvaluekeeps its stored value when the stored header with the same key is also secret. A new header, or one that stops being secret, needs avalue. The signing secret never changes here.
Response shaping
fields:optionalstringComma-separated list of fields to return. 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: config, created_at, execution_order, handler_name, id, is_enabled, phase, project_id, updated_at, workflow_transition_id.
See Sparse fields.
Scopes
workspaces.workflows:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | The body has phase or handler_name, or config failed validation. |
401 | unauthorized | Missing or invalid credentials. |
402 | payment_required | Workflows isn't on your plan, or the hook's handler feature isn't: Plane Runner for run_script, Webhook transition conditions for send_webhook. |
403 | forbidden | Your role or token scope can't edit workspace workflows. |
404 | not_found | No such workflow, transition, or hook, or the hook was deleted. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
409 | governance_migration_in_progress | A governance migration is running in this workspace, so writes are paused. Retry after it finishes. |
409 | states_managed_at_project | The workspace doesn't have governance turned on. Use the project's workflows instead. |
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 PATCH \
"https://api.plane.so/api/v2/workspaces/my-team/workflows/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/state-transitions/d7a3f5c9-0b2e-4d81-b6f4-9e1c3a5d7b20/hooks/e4b7c9a2-3d1f-4e68-8a52-9f0c6b3d7e15/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"is_enabled": false
}'import requests
response = requests.patch(
"https://api.plane.so/api/v2/workspaces/my-team/workflows/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/state-transitions/d7a3f5c9-0b2e-4d81-b6f4-9e1c3a5d7b20/hooks/e4b7c9a2-3d1f-4e68-8a52-9f0c6b3d7e15/",
headers={"X-Api-Key": "your-api-key"},
json={
"is_enabled": False,
},
)
print(response.json())const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/workflows/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/state-transitions/d7a3f5c9-0b2e-4d81-b6f4-9e1c3a5d7b20/hooks/e4b7c9a2-3d1f-4e68-8a52-9f0c6b3d7e15/",
{
method: "PATCH",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
is_enabled: false,
}),
},
);
const data = await response.json();{
"id": "e4b7c9a2-3d1f-4e68-8a52-9f0c6b3d7e15",
"workflow_transition_id": "d7a3f5c9-0b2e-4d81-b6f4-9e1c3a5d7b20",
"project_id": null,
"phase": "post",
"handler_name": "send_webhook",
"is_enabled": false,
"execution_order": 1,
"config": {
"url": "https://hooks.example.com/plane",
"headers": [
{
"key": "X-Team",
"value": "platform",
"is_secret": false
}
]
},
"created_at": "2026-01-15T11:04:52.660219Z",
"updated_at": "2026-02-03T16:45:12.904117Z"
}{
"type": "invalid_request",
"code": "invalid_request",
"detail": "`phase` is immutable after creation."
}
