> ## Documentation Index
> Fetch the complete documentation index at: https://flywheel.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Flywheel MCP

> Connect Claude, ChatGPT, Cursor or any MCP client to Flywheel, and what an agent can do once it is connected

## What the Flywheel MCP is

Flywheel runs a remote [Model Context Protocol](https://modelcontextprotocol.io) server at `https://api.flywheel.cx/mcp`. An AI agent connected to it works inside one Flywheel workspace with the same building blocks the dashboard uses: people and their properties, segments, workflows, webinars, broadcasts and Signals.

The MCP tools are thin wrappers over the same code as the `/v1` REST API and the dashboard, so a workflow an agent builds is the same workflow you see in the builder, and a broadcast it plans is checked exactly as one planned in the dashboard.

The server is stateless Streamable HTTP: every request is one JSON-RPC message sent with `POST`, answered with JSON. It offers tools only (no MCP resources or prompts). The documentation itself is reachable through tools: `search_docs`, `get_doc` and `list_docs`.

## Connecting an MCP client

Clients that support OAuth need only the URL. Flywheel publishes OAuth discovery and open client registration, so the client registers itself, opens a browser, you sign in to Flywheel, pick the workspace and approve once. The client then holds its own token.

| Client | How to add Flywheel |
| - | - |
| Claude (claude.ai, desktop) | Settings → Connectors → Add custom connector, URL `https://api.flywheel.cx/mcp` |
| Claude Code | `claude mcp add --scope user --transport http flywheel https://api.flywheel.cx/mcp`, then `claude mcp login flywheel` |
| Codex | `codex mcp add flywheel --url https://api.flywheel.cx/mcp`, then `codex mcp login flywheel` |
| Cursor | `~/.cursor/mcp.json`: `{ "mcpServers": { "flywheel": { "url": "https://api.flywheel.cx/mcp" } } }`, then Connect in Cursor Settings → MCP |
| VS Code | `code --add-mcp '{"name":"flywheel","type":"http","url":"https://api.flywheel.cx/mcp"}'` (VS Code config uses the key `servers`, not `mcpServers`) |
| Gemini CLI | `gemini mcp add --transport http flywheel https://api.flywheel.cx/mcp`, then `/mcp auth flywheel` (settings.json uses `httpUrl`, not `url`) |
| Windsurf | `~/.codeium/windsurf/mcp_config.json`: `{ "mcpServers": { "flywheel": { "type": "streamable-http", "serverUrl": "https://api.flywheel.cx/mcp" } } }` |
| Clients that only run local servers | `{ "mcpServers": { "flywheel": { "command": "npx", "args": ["-y", "mcp-remote", "https://api.flywheel.cx/mcp"] } } }` |

The dashboard's **Settings → MCP** page shows the same instructions with copy buttons, and lists the apps connected to your workspace so you can revoke them.

## Connecting with an API key instead of OAuth

For a client or script that cannot do OAuth, use an organization API key from **Settings → API**. It needs **both** headers; without `Auth-Type: api` the key is read as a dashboard session and refused.

```json theme={null}
{
	"mcpServers": {
		"flywheel": {
			"type": "http",
			"url": "https://api.flywheel.cx/mcp",
			"headers": { "Authorization": "Bearer <YOUR_API_KEY>", "Auth-Type": "api" }
		}
	}
}
```

Check a key works from a terminal:

```bash theme={null}
curl -sS https://api.flywheel.cx/mcp \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Auth-Type: api" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

<Note>
  Keys created before MCP access existed are refused with "This API key was created before MCP access existed". Create a new key, or connect with OAuth.
</Note>

## Permissions and which tools an agent sees

Every tool requires one permission, such as `people.read`, `workflows.publish` or `broadcasts.send`. A connection only sees (in `tools/list`) and can only call the tools its permissions cover, so an agent plans with the tools it can actually use. A tool called without the permission returns a result saying which permission is missing rather than failing silently.

| Family | Read | Change | Highest-impact |
| - | - | - | - |
| People | `people.read` | `people.write` (contacts, properties, segments) | `people.delete` (delete a person and their history) |
| Workflows | `workflows.read` | `workflows.write` (drafts) | `workflows.publish` (publish, turn live) |
| Webinars | `webinars.read` | `webinars.write` (sessions, series, registrations, attendance) | |
| Broadcasts | `broadcasts.read` | `broadcasts.write` (drafts, plans) | `broadcasts.send` (schedule) |
| Signals | `signals.read` | `signals.write` (rules, triggers, actions, reviewer) | |

The documentation tools (`search_docs`, `get_doc`, `list_docs`) read no workspace data and are available to every connection.

## What reaches real people

Most tools only read or edit drafts. These are the ones whose effect leaves Flywheel, and an agent should show the person it works for what will happen and get a clear yes before calling them:

| Tool | What it does to real people |
| - | - |
| `set_workflow_status` (to live) | A published workflow starts reacting to events and sending its messages. |
| `publish_workflow` on a live workflow | The new version takes over for everyone it runs for. |
| `schedule_broadcast` | Commits a planned broadcast; it goes out at its send time (always at least an hour away, and it can be pulled back with `unschedule_broadcast` until then). |
| `register_for_webinar` | Registers someone and starts the session's sequences (confirmation, reminders). |
| `set_webinar_attendance` | Marks attended or no-show, which starts the follow-up sequences bound to that outcome. |
| `update_webinar` | Rescheduling moves the session's planned messages; cancelling stops the session's own messages and refuses new registrations (sequence runs people are already in keep going). |
| `set_opt_outs` | Changes whether a person can be messaged on a channel. |
| `delete_person` | Removes a person and their history. |
| `commit_import` | Creates and updates many people at once, can opt them out of marketing email, and with `merges: "allow"` merges existing people (which cannot be undone). Preview it with `preview_import` first. |

MCP clients see these as non-read-only tools, and the irreversible ones carry the destructive hint, so a client that asks before running tools will ask here.

## How an agent should work with Flywheel

* **Read before you write.** List or get the thing first (`list_workflows`, `get_webinar`, `get_segment`). Most changes are patches that keep what you leave out.
* **Never invent ids.** Every id is a UUID read from a list or get tool. The only ids an agent makes up are fresh v4 UUIDs for new workflow nodes.
* **Resolve a person before acting on one** with `search_people`, or `identify_person` when they may not exist yet.
* **Use the vocabulary.** Workflow params may only reference event names, properties, senders and other ids from `get_vocabulary`; anything else fails validation.
* **Let validation and plans do the checking.** `create_workflow` and `update_workflow` return validation findings for what they saved; `plan_broadcast` shows exactly who gets a broadcast and what it says. Fix what they report rather than working around it.
* **Confirm before anything in "What reaches real people".**
* **Look it up.** When unsure how something works, call `search_docs` before guessing.

## Searching the documentation from an agent

Three tools serve this documentation site to a connected agent:

* `search_docs` takes a plain-language `query` and returns the best-matching doc sections (page `path`, `section` heading, `url`, a snippet) and matching tools (with the permission each needs). Filter with `kind: "doc"` or `kind: "tool"`.
* `get_doc` returns a page as markdown, or just one `section` of it. Pass the `path` from a search result, for example `{ "path": "mcp/workflow-definitions", "section": "Validation finding codes" }`. A pasted docs URL works too.
* `list_docs` returns the table of contents: every page's path, title, description and headings. Narrow it with `area`, for example `{ "area": "mcp" }`.

```json theme={null}
{ "name": "search_docs", "arguments": { "query": "branch a workflow on a custom property" } }
```

## Map of the MCP guides

| Page | Read it for |
| - | - |
| [People, properties and segments](/mcp/people) | Finding and identifying people, identifiers, custom properties, segment filters, opt-outs, imports |
| [Building workflows](/mcp/workflows) | The draft → validate → publish → live loop, runs and debugging |
| [Workflow definitions](/mcp/workflow-definitions) | Triggers, actions, exit criteria, `next` wiring, context references, finding codes, complete examples |
| [Broadcasts](/mcp/broadcasts) | Audiences, variables, plan and schedule |
| [Webinars](/mcp/webinars) | Sessions, series, registrants, attendance, the send plan, platform links |
| [Signals](/mcp/signals) | The AI reviewer: rules, triggers, actions, event dictionary, settings |
| [Tool reference](/mcp/tools) | Every tool, its permission and its parameters |

## Errors and what they mean

| What you see | Meaning | What to do |
| - | - | - |
| HTTP 401 with `WWW-Authenticate` | No credential, or it expired | OAuth clients sign in again; key users check both headers |
| HTTP 403 "accepts OAuth access tokens and organization API keys only" | A dashboard session token was sent | Use OAuth or an org API key |
| HTTP 403 "created before MCP access existed" | Legacy API key | Create a new key |
| Tool result "Permission denied: … requires the '…' permission" | The connection lacks that permission | Reconnect with that access |
| JSON-RPC error `-32602` "Invalid arguments for …" | Arguments failed the tool's schema; the message names each field | Fix the named fields |
| JSON-RPC error `-32602` "Unknown tool" | No tool by that name (or a typo) | Call `tools/list` or `search_docs` with `kind: "tool"` |
| Tool result with `isError: true` | The tool ran and refused or failed, with the reason | Read the message; it is written for the agent to act on |
| HTTP 400 "batching is not supported" | A JSON array of messages was sent | Send one message per request |
| HTTP 405 on GET | The server is stateless; there is no event stream | Use POST |

## MCP or the REST API

Use the MCP when an AI agent is doing the work: tools are discoverable, described and permission-filtered, and the docs are searchable from inside the session. Use the REST API (`/v1`, see the API tab) from your own code, for example to [send events](/api-reference/events) or [register webinar attendees](/api-reference/webinars) from your signup form. Both act on the same data through the same implementation.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.