building-workflows
Building workflows
A PostHog workflow is a directed graph: a list of action nodes (actions) wired by edges (edges), with exactly one trigger node that starts every run. You author that graph as JSON and ship it over MCP. Always call it a "workflow" to the user. "Hog flow" is the internal code name (HogFlow), not a user-facing term.
The single biggest failure mode is getting the graph JSON structurally wrong. The backend stores actions/config as loose JSON, but the visual editor parses every node against a strict schema, so a malformed node saves but then breaks the editor view for the whole workflow. Before composing or editing any graph, read references/graph-schema.md. It is the contract; do not improvise node shapes from these examples alone.
The lifecycle
Work the workflow through these stages. Don't jump straight to enabling it.
- Compose the graph. Build
actions+edgesper references/graph-schema.md. For anyfunctionnode, don't guess the template: list the live catalog withcdp-function-templates-listand read its required inputs withcdp-function-templates-retrieve. - Create as a draft.
workflows-create. Every workflow is createddraft; it does not execute yet. - Test-run it.
workflows-test-runruns one step at a time. Start at the first step (omitcurrent_action_id, or point it at the trigger) with sampleglobals({event, person, groups}); the result includes the next step's id (nextActionId). Feed that back ascurrent_action_idand run again, walking step by step to the end. Skipdelaynodes by jumping to the action after them (delays aren't simulated). Async side effects (HTTP/email/SMS) are mocked unless you setmock_async_functions=false. Read each step's trace to confirm the path taken. - Read logs while iterating.
workflows-logsshows the per-step execution trace (levels DEBUG to ERROR). This is how you see why a step skipped, branched, or errored. - Edit the draft, then re-test. Patch the graph with
workflows-patch-graph(see Editing a draft). Every edit invalidates your earlier test — re-run the affected path before moving on. Drafts only; active workflows are read-only over MCP. - Enable (one-way door, needs the user's explicit sign-off).
workflows-enableflips it toactiveand an event/webhook/manual trigger starts firing on matching activity. You can't edit a live workflow over MCP (to change it you recreate it as a new draft), so treat enabling as effectively irreversible: finish testing, then get the user's explicit go before enabling. Don't enable on your own initiative. - Dispatch (batch/schedule only). A
batchworkflow does not fire on enable alone. Send a one-off broadcast withworkflows-run-batch, or attach a recurring schedule withworkflows-schedule-create. Confirm withworkflows-getthatstatus=='active'and its read-onlyschedulesfield has an active entry. - Monitor. Drill down:
workflows-global-stats(which workflows are failing) toworkflows-stats(one workflow's trend) toworkflows-list-invocations(who it failed for) toworkflows-get-invocation(the triggering payload) toworkflows-logs(the failing step).