Skip to content

Workflow hooks overview ​

A workflow hook is a side effect attached to one workflow transition. When a work item moves along that transition, Plane runs the hook's handler, either before the move or after it.

There are two handlers:

  • send_webhook sends the transition event to a URL you own, signed with a secret Plane generates.
  • run_script runs a Plane script with the event as input.

A hook's phase decides when it runs. A pre hook runs before the move, and a failure blocks the transition. A post hook runs after the move, and a failure does not undo or block it.

Learn more about workflows

The workflow hook object ​

Attributes ​

  • id string (uuid)

    Unique identifier for the hook.

  • workflow_transition_id string (uuid)

    The transition the hook is attached to.

  • project_id string (uuid)

    The project the hook belongs to. Always set on these project routes. Hooks on catalog transitions in a governed workspace have null here.

  • phase string

    When the hook runs. pre runs before the move and can block it. post runs after the move. Set at create time and immutable.

  • handler_name string

    What the hook does. One of send_webhook or run_script. Set at create time and immutable.

  • is_enabled boolean

    Whether the hook runs. A disabled hook stays attached to the transition but is skipped.

  • execution_order integer

    Position of the hook among the hooks of the same phase on this transition. Plane assigns it at create time, one past the highest existing value for that phase. It is not writable.

  • config object

    The handler's settings. For send_webhook, it holds url and headers. For run_script, it holds script_id and execution_variables. The webhook signing secret is never part of a read. See The config object.

  • created_at string (date-time)

    When the hook was created.

  • updated_at string (date-time)

    When the hook was last updated.

Response200
json
{
  "id": "3b7e9a15-6c2d-4f84-9b31-0e5d8c7a2f69",
  "workflow_transition_id": "5c8d2e71-4a9f-4b36-8e05-d17f3a6b9c42",
  "project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
  "phase": "post",
  "handler_name": "send_webhook",
  "is_enabled": true,
  "execution_order": 1,
  "config": {
    "url": "https://hooks.example.com/plane/transitions",
    "headers": [
      {
        "key": "X-Team",
        "value": "platform",
        "is_secret": false
      }
    ]
  },
  "created_at": "2026-01-14T09:22:41.478363Z",
  "updated_at": "2026-01-14T09:22:41.478363Z"
}

The config object ​

The shape of config depends on handler_name.

send_webhook ​

KeyTypeNotes
urlstringRequired. The destination, http or https only. Plane rejects URLs that resolve to private or internal addresses and any domain the instance disallows for webhooks.
headersarrayOptional. Up to 20 extra request headers, each { "key", "value", "is_secret" }. key is 1 to 256 characters and unique without regard to case. value is up to 8192 characters. is_secret defaults to true.

These header names are reserved and rejected: host, content-length, content-type, user-agent, x-plane-delivery, x-plane-event, and x-plane-signature.

Plane stores the value of a secret header encrypted. Reads return the stored encrypted string, not the value you sent, so keep the original on your side if you need it.

run_script ​

KeyTypeNotes
script_idstringRequired on create. A system script, or a script in this workspace that is either workspace-wide or belongs to this project.
execution_variablesobjectOptional. Variables passed to the script when it runs.

The webhook secret ​

When you create a send_webhook hook, Plane generates a signing secret of the form plane_wh_ followed by 32 hex characters. The create response is the only read that carries it, as config.secret_plaintext. Every later read omits it.

Each delivery is a POST with a JSON body and these headers:

  • X-Plane-Signature, the HMAC-SHA256 hex digest of the body computed with the secret. Recompute it on your side to verify that the request came from Plane.
  • X-Plane-Event, the event type, workflow.transition.
  • X-Plane-Delivery, a unique id for the delivery.

If you lose the secret, or need to rotate it, call Regenerate a workflow hook secret. The old secret stops validating immediately.

Execution history ​

Plane records every run of a hook. List workflow hook executions returns the runs with their status, input, and output. Deleting a hook does not delete its history.

Entitlements ​

Each handler has its own plan feature. Creating a hook, or updating an existing one, returns 402 payment_required when the handler's feature isn't enabled for the workspace. The check runs on update too, so a hook created before a downgrade can't be edited afterward.

Endpoints ​

MethodPathDescription
GET/api/v2/workspaces/{slug}/projects/{project_id}/workflows/{workflow_id}/state-transitions/{transition_id}/hooks/List workflow hooks
GET/api/v2/workspaces/{slug}/projects/{project_id}/workflows/{workflow_id}/state-transitions/{transition_id}/hooks/{pk}/Get a workflow hook
POST/api/v2/workspaces/{slug}/projects/{project_id}/workflows/{workflow_id}/state-transitions/{transition_id}/hooks/Create a workflow hook
PATCH/api/v2/workspaces/{slug}/projects/{project_id}/workflows/{workflow_id}/state-transitions/{transition_id}/hooks/{pk}/Update a workflow hook
DELETE/api/v2/workspaces/{slug}/projects/{project_id}/workflows/{workflow_id}/state-transitions/{transition_id}/hooks/{pk}/Delete a workflow hook
GET/api/v2/workspaces/{slug}/projects/{project_id}/workflows/{workflow_id}/state-transitions/{transition_id}/hooks/{pk}/executions/List workflow hook executions
POST/api/v2/workspaces/{slug}/projects/{project_id}/workflows/{workflow_id}/state-transitions/{transition_id}/hooks/{pk}/regenerate-webhook-secret/Regenerate a workflow hook secret

Governed workspaces ​

In a workspace with workspace governance turned on, workflows and their transitions live in the workspace catalog and have no project. These project routes look the transition up by project, so they return 404 there. Manage hooks on catalog transitions with Workspace workflow hooks. Those routes use the same object and the same rules. They return 409 states_managed_at_project in an ungoverned workspace.

Activity ​

Creating, updating, or deleting a hook adds an entry to the workflow's audit trail with field set to transition_rule. See Workflow activities.

Response shaping ​

Every hook read accepts ?fields= for sparse responses. See Sparse fields. Errors follow the shared problem+json contract.