Skip to content
Development
Skill

/building-workflows

Build, edit, test, enable, and monitor PostHog workflows over MCP. Author the action/edge graph so it runs and opens cleanly in the visual editor, then change drafts surgically with patch operations. Use when asked to build, set up, automate, change, fix, or debug a workflow,

From plugin
ai-plugin
95180 skills1 agent3 commands3 hooks
+1

Claude Code

$ npx -y skills add PostHog/ai-plugin --skill building-workflows --agent claude-code

SKILL.md

building-workflows.SKILL.md
name: building-workflows
description: 'Build, edit, test, enable, and monitor PostHog workflows over MCP. Author the action/edge graph so it runs and opens cleanly in the visual editor, then change drafts surgically with patch operations. Use when asked to build, set up, automate, change, fix, or debug a workflow, campaign, broadcast, drip sequence, or event-triggered automation in the workflows product, including first-time messaging setup (email channel, sending domain, frequency cap) before the first workflow.'

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](references/graph-schema.md). It is the contract; do not improvise node shapes from these examples alone.

A one-off email that the user's code sends to one person, such as a password reset, a sign-in code or a receipt, is a transactional email, not a workflow. Use the `sending-transactional-emails` skill for it.

Before the first messaging workflow

When the user asks to set up workflows or messaging, or asks for an email, SMS or push workflow in a project that may not send yet, call `workflows-setup-status` before composing anything. It says which setup steps are open and links each one in the app. Walk the user through the open steps, set the sending rules, then offer suggested workflows. Read [references/onboarding.md](references/onboarding.md) for the flow and the defaults to recommend.

Skip this for automations that only post to Slack or call webhooks: they need no email setup. If `workflows-setup-status` is not available, go straight to the lifecycle below.

The lifecycle

Work the workflow through these stages. Don't jump straight to enabling it.

1. **Compose the graph.** Build `actions` + `edges` per [references/graph-schema.md](references/graph-schema.md). For any `function` node, don't guess the template: list the live catalog with `cdp-function-templates-list` and read its required inputs with `cdp-function-templates-retrieve`. 2. **Create as a draft.** `workflows-create`. Every workflow is created `draft`; it does not execute yet. 3. **Test-run it.** `workflows-test-run` runs **one step at a time**. Start at the first step (omit `current_action_id`, or point it at the trigger) with sample `globals` (`{event, person, groups}`), shaped like the trigger's real payload: an `event` trigger needs an event matching its filters, and an `internal-event` trigger needs an event named in its `filters.events` (for the Slack trigger, a `$slack_message_received` event with the Slack property bag) and no person (see [references/graph-schema.md](references/graph-schema.md)). A `status=skipped` result means this payload would not fire the trigger: if you fabricated the payload, fix it to match the trigger; if it came from a real past run (`workflows-get-invocation`), the trigger's filter is wrong, not the payload. The result includes the next step's id (`nextActionId`). Feed that back as `current_action_id` and run again, walking step by step to the end. Skip `delay` nodes by jumping to the action after them (delays aren't simulated). Async side effects (HTTP/email/SMS/push) are mocked unless you set `mock_async_functions=false`. Read each step's trace to confirm the path taken. 4. **Read logs while iterating.** `workflows-logs` shows the per-step execution trace (levels DEBUG to ERROR). This is how you see _why_ a step skipped, branched, or errored. 5. **Edit, then re-test.** Patch the graph with `workflows-patch-graph` (see [Editing a draft](#editing-a-draft)). **Every edit invalidates your earlier test** — re-run the affected path before moving on. On a draft workflow, edits apply directly; on an active one they stage a draft (see [Changing a live workflow](#changing-a-live-workflow)). 6. **Enable (needs the user's explicit sign-off).** `workflows-enable` flips it to `active` and an **event/webhook/manual** trigger starts firing on matching activity. From then on it runs on real people, and every change goes through the draft → test → publish cycle before taking effect — so finish testing, then get the user's explicit go before enabling. Don't enable on your own initiative. 7. **Dispatch (batch/schedule only).** A `batch` or `schedule` workflow does **not** fire on enable alone. Send a one-off broadcast with `workflows-run-batch`, or attach a recurring schedule with `workflows-schedule-create`. A `batch` trigger fans out to a person audience, so scheduling it needs the `workflows-blast-radius` preview and its confirm token; a `schedule` trigger runs once per occurrence with no audience, so schedule it directly. Confirm with `workflows-get` that `status=='active'` _and_ its read-only `schedules` field has an active entry. 8. **Monitor.** Drill down: `workflows-global-stats` (which workflows are failing) to `workflows-stats` (one workflow's trend) to `workflows-list-invocations` (who it failed for) to `workflows-get-invocation` (the triggering payload) to `workflows-logs` (the failing step).

Full tool catalog, grouped by job: [references/lifecycle-and-debugging.md](references/lifecycle-and-debugging.md).

Editing a draft

**Patch, don't replace.** Edit a draft with `workflows-patch-graph`: a small, ordered list of id-addressed operations (`update_action`, `add_action`, `remove_action`, `add_edge`, `remove_edge`, `replace_action_edges`). `update_action` deep-m

Read more
@posthog
Ships withai-plugin

Official PostHog plugin for AI clients. Access PostHog products directly from your AI coding tool.

95180 skills1 agent1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
95
Star history of PostHog/ai-plugin on GitHub, a chart by star-history.com

Other skills on ai-plugin.

analyzing-ai-citations

analyzing-ai-citations

Answer AI citation and answer engine optimization (AEO) questions in PostHog: whether AI answer engines (ChatGPT, Claude, and an…

@posthog@posthogInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about building-workflows.

Which coding agents can use building-workflows?
building-workflows installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.