search_docs and kind: "tool".
People, properties and segments
search_people
Permission:people.read · Effect: read-only
Search people by any identifier value (email, phone, external id, Stripe id, a full_name identifier): a case-insensitive substring match on identifier values only, so the first_name / last_name fields are not searched. The fastest way to resolve a person when you have something a human typed. Returns up to 50 person summaries, unordered; use get_person for full detail.
list_people
Permission:people.read · Effect: read-only
List people with exact filters (subscription status, MRR/LTV ranges, health band, segment membership, identified-only, and what they did: did_event / did_not_event) and pagination. Event conditions query a cohort without saving a segment, e.g. did_event [{event:‘signed_up’, within_days:30}] with did_not_event [{event:‘subscription_started’}]. For free-text lookup use search_people instead.
get_person
Permission:people.read · Effect: read-only
Fetch one person in full: names, primary contact details, subscription standing (status, MRR, LTV, payment dates), engagement recency, custom properties, contact methods, and assigned team members.
identify_person
Permission:people.write · Effect: changes data
Find or create a person by any identifier (email, phone, external user id, Stripe customer id, anonymous id), through the same pipeline integrations use, including duplicate-contact merging. Idempotent: repeating the call returns the same person. Start here when you are not sure a person exists yet. Resolves by external_user_id first, then stripe_customer_id, email, anonymous_id, phone; at least one is required (a name alone cannot identify anyone). external_user_id must be the org’s real user id for this person, read from their product, never a guess: it is attached as an identifier and never replaces the id the person already has.
delete_person
Permission:people.delete · Effect: changes data, destructive or reaches people
Permanently delete one person and everything attached to them: their events, sent-email history, subscriptions, webinar registrations and signals. It cannot be undone. A person with an opt-out or a bounce on record is refused, because their suppression would go with them and they would come back mailable if identified again; it is also refused while tasks or connection invites reference the person. Resolve the person first and confirm with the person you are working for before calling it. Every call is recorded with this connection’s credential.
list_identifiers
Permission:people.read · Effect: read-only
List every identifier attached to a person (emails, phones, external ids, Slack ids …) with primary/contact-method flags.
add_identifier
Permission:people.write · Effect: changes data
Attach an identifier to a person. Conflict-safe: if the value already belongs to another person the result reports the conflict instead of silently duplicating. Emails are lowercased.
update_identifier
Permission:people.write · Effect: changes data
Edit an identifier: its value or its contact-method / primary flags. Promoting to primary demotes the previous primary of that type.
remove_identifier
Permission:people.write · Effect: changes data, destructive or reaches people
Delete one identifier from a person. The person remains; only this way of reaching or recognizing them goes.
list_property_configs
Permission:people.read · Effect: read-only
The org’s custom property definitions (key, name, type). Values and segment filters both reference these keys — call this before set_person_property or create_segment with custom properties.
No parameters.
create_property_config
Permission:people.write · Effect: changes data
Define a new custom property (key + display name + type). Do this once per property; then set per-person values with set_person_property.
update_property_config
Permission:people.write · Effect: changes data
Rename a custom property or edit its description. Key and type are immutable — existing values and segment filters depend on them.
delete_property_config
Permission:people.write · Effect: changes data, destructive or reaches people
Delete a custom property definition AND every per-person value stored under it. Irreversible for the stored values — read it back first if in doubt.
get_person_properties
Permission:people.read · Effect: read-only
The custom property values set on one person, keyed by property key.
set_person_property
Permission:people.write · Effect: changes data
Set (or overwrite) one custom property value on a person. The key must already have a property config and the value must match its type — create the config first if it doesn’t exist.
delete_person_property
Permission:people.write · Effect: changes data, destructive or reaches people
Remove one custom property value from a person entirely (the property config itself stays).
list_segments
Permission:people.read · Effect: read-only
Every saved segment (dynamic filtered group of people) with its conditions in public form. System segments are read-only.
No parameters.
get_segment
Permission:people.read · Effect: read-only
One segment with its filter conditions.
list_segment_members
Permission:people.read · Effect: read-only
The people currently matching a segment, newest first, evaluated live by the same engine the dashboard and broadcast sends use. total is the full member count; page with limit/offset. For segment members that also match other filters, use list_people with segment_id.
create_segment
Permission:people.write · Effect: changes data
Create a segment from filter conditions (ANDed). Filters reference built-in person fields, custom property keys (call list_property_configs first), or events a person did (“signed_up at least twice in the last 30 days”, “never invoice_paid”). Date and event-window values may be relative tokens (now-30d) that stay rolling. The result shows the conditions as stored; verify membership with list_segment_members.
update_segment
Permission:people.write · Effect: changes data, destructive or reaches people
Edit a segment. Passing filters REPLACES the whole condition set — send the complete list you want, not a delta. System segments cannot be edited.
delete_segment
Permission:people.write · Effect: changes data, destructive or reaches people
Delete a segment definition. People are untouched — only the saved grouping goes. System segments cannot be deleted.
list_subscriptions
Permission:people.read · Effect: read-only
A person’s subscriptions as mirrored from the payment processor: items, cost, status, period and trial dates. Read-only.
get_opt_outs
Permission:people.read · Effect: read-only
A person’s marketing opt-out status for email and SMS. No record means fully opted in.
set_opt_outs
Permission:people.write · Effect: changes data
Set marketing opt-outs for a person. Opting someone out stops marketing email/SMS to them — honor requests immediately. Opting back IN should only follow explicit consent from the person.
list_data_imports
Permission:people.read · Effect: read-only
CSV data-import history, newest first: status, file name, total_rows and processed_rows (successes and failures combined).
No parameters.
create_import
Permission:people.write · Effect: changes data
Start a people import (contacts from a CSV, spreadsheet or another tool). Parse the file yourself and send its column names plus up to 20 sample rows (objects keyed by column). Creates a draft — nothing is written to people yet — and returns, per column, what the values look like and a suggested target when the header and values agree, plus every valid target (person fields, consent columns, existing properties). Next: set_import_mapping.
set_import_mapping
Permission:people.write · Effect: changes data
Decide what every column of a draft import becomes. Every column must be mapped or “skip”. A target is a person field (email, alternate_email, phone, alternate_phone, external_user_id, stripe_customer_id, anonymous_id, first_name, last_name), a consent column (subscribed_to_emails or unsubscribed_from_emails — these only ever opt people OUT), { “property”: “<existing key>” }, or { “new_property”: { “key”: “snake_case”, “name”: ”…”, “type”: “string|number|boolean” } } to declare a new one. At least one identifier is required. Refuses unknown property keys (with the closest match), system keys, two columns on one target, and sample values that do not fit. Returns issues and the first sample rows as the person each becomes. Only saved when there are no errors.
append_import_rows
Permission:people.write · Effect: changes data
Add rows to a draft import after its mapping is set, in file order, up to 500 per call (50000 per import). Each row is an object keyed by column. Every row is checked against the mapping; rows that do not fit are rejected with the row number and reason and are not imported. Blank cells are ignored, never written over existing values. Call once per chunk, one call at a time.
preview_import
Permission:people.read · Effect: read-only
Dry run of a draft import — writes nothing. Says how many people it will create and update, which existing property values change, how many people get opted out, which new properties get created, and which rows would MERGE two or more existing people into one (a merge deletes all but one and cannot be undone). merges: “block” (default) refuses to produce a token while merges exist; “skip_rows” leaves those rows out; “allow” merges them. Returns a commit_token when ready. Show this preview to the person you are working for before committing.
commit_import
Permission:people.write · Effect: changes data, destructive or reaches people
Run a previewed import with its commit_token. Re-checks everything and refuses if anything changed since the preview (re-preview then). Creates the declared new properties, then queues the rows; people are created and updated over the next minutes. Only after the person you are working for has seen preview_import and said go. Follow it with get_import.
get_import
Permission:people.read · Effect: read-only
One import’s status, mapping and counts (staged, succeeded, failed), with the reason for each failed row (latest 100).
cancel_import
Permission:people.write · Effect: changes data
Delete a draft import and its staged rows. Only drafts: a committed import cannot be cancelled.
events
track_event
Permission:events.ingest · Effect: changes data, destructive or reaches people
Record events that people did (1–100 per call), synchronously: each comes back with a status — inserted, duplicate (that event_id is already stored; a safe retry), event_id_conflict (another organization uses that id; pick another), invalid (refused before anything was written; see error) or failed (retry with the same event_id) — plus the stored row id and the person it landed on. An inserted event can carry a warning (stored, but starting its workflows failed): do not re-send it; tell the person you work for. THIS CAN REACH REAL PEOPLE: every live workflow triggered on the event name enrolls the person, even when the timestamp is in the past. Before backfilling history, check which live workflows listen on that name (list_workflows / get_workflow) and confirm with the person you work for. Name each person one way: person_id (resolve with search_people first), or user_id / email / anonymous_id. An unknown person is refused unless create_person: true. track_event never changes a person’s identifiers or properties — use identify_person / set_person_property for that. Always pass a stable event_id so retries cannot double-record. At most 5 event names the org has never recorded may be introduced per call (new_event_names lists them) — a new name creates a new event type, so check spelling against list_events first. Limit: 600 events per minute per organization.
list_events
Permission:events.read · Effect: read-only
Read the org’s events, newest first, a page at a time (next_cursor). With person_id: that person’s history, ordered by when each event happened. Without it: the org’s feed, ordered by when Flywheel received each event, and limited to events received in the last 7 days (before until, if given) unless you pass since — the response’s received_since says where that window starts. Filter by event name(s), a time window on when events happened (since / until), or how they arrived (location: fw_api, fw_mcp, fw_pixel, …). The pixel’s autocapture events ($ac_*) are left out unless you name one or pass include_autocapture. Large properties come back cut down (properties_truncated); pass include_properties: false to skip them. Use it to check what names an org records before track_event, to confirm an event landed, or to see what a person did.
Workflows
list_workflows
Permission:workflows.read · Effect: read-only
List every workflow (automation) in the organization: id, name, status (live / paused / null for never live), published version.
No parameters.
get_workflow
Permission:workflows.read · Effect: read-only
Fetch one workflow: definition is the published version (what runs), draft the current draft (what the builder shows and what publish_workflow publishes), both in the database-row form the write tools take, with node labels. has_unpublished_changes says whether the draft would run differently; draft_placeholders lists unfilled “Add step” nodes that block publishing; draft_hash fingerprints the draft for update_workflow_node. Email bodies make this large: view “summary” replaces each body (template_content, template_content_json, html, editor JSON) with { omitted: true, bytes } and keeps subject, preview text, template id and variables — use it unless you are changing a body, and include “draft” or “definition” to get one half. A summary is for reading: never send it back through update_workflow (the write refuses omitted bodies); change single steps with update_workflow_node, or read view “full” before editing bodies.
list_workflow_runs
Permission:workflows.read · Effect: read-only
List runs of a workflow, newest first, with total (every run matching the filter, not just this page). status filters by run status; “stuck” lists in_progress runs with no activity for 30 minutes (no log, no step moving, no wait still to end, including a webinar wait whose session moved later), each with last_activity_at and last_node_id (where it stopped) — the ones to look at with get_run and then cancel_run / retry_run.
get_run
Permission:workflows.read · Effect: read-only
Fetch one workflow run: its status and attempt, step-by-step logs (each with node_id / node_type, the node’s id as get_workflow and retry_run name it), and steps, the run’s step ledger (which steps were dispatched, are running, are waiting and until when, finished or failed). include_context: false leaves out the run context, which can be tens of KB.
get_node_types
Permission:workflows.read · Effect: read-only
The workflow engine’s node-type catalog: every trigger, action, and exit-criteria type with JSON Schemas for its params (per version), the params paths that may hold a {{context.<source>.<path>}} reference (context_ref_fields, per version; a reference anywhere else is refused with FIELD_NOT_EDITABLE), its next wiring shape, and what it contributes to run context. Generated from the same schemas validation enforces. Call this FIRST, before building or editing a workflow definition; never use a type marked executable: false.
get_vocabulary
Permission:workflows.read · Effect: read-only
The organization’s referenceable vocabulary: event names, custom property configs, email/SMS sender ids, health score rule ids, webinar ids, team member user ids, and email template ids. Workflow params referencing ids outside this vocabulary fail validation — read it before writing params, never invent ids. Keys: eventNames, customPropertyConfigs (id, name, key), senderConfigIds, smsSenderIds, healthScoreRuleIds, webinarIds, webinarSeriesIds, orgMemberUserIds, templateIds. Pixel autocapture events ($ac_*) are left out of event names to keep the list readable; they are still valid triggers.
No parameters.
get_node_context
Permission:workflows.read · Effect: read-only
For one node of a saved workflow (its draft first, else its published version): which upstream nodes’ context exists when it runs and the JSON Schema of what each contributes, plus the always-available root context. Use this to pick real {{context.<source_id>.<path>}} references instead of guessing.
validate_workflow
Permission:workflows.read · Effect: read-only
Dry-run validation of a workflow definition in database-row form. Runs all three tiers (structure + schemas, context references, org vocabulary) and returns structured findings with stable codes. Never writes. It does NOT run the save-time checks create/update/publish run first (lowercase UUID ids, builder-drawable conditions, trigger_condition must be null), so valid: true can still be refused at save. Pass exit_criteria: [] when there are none. create_workflow / update_workflow return the same findings for what they save, so this is for checking a definition before saving it.
create_workflow
Permission:workflows.write · Effect: changes data
Create a workflow as a DRAFT, laid out on the builder canvas so a person can open it. Order of work: get_node_types (params + next shape of each type) → get_vocabulary (ids and event names params may use) → this. The draft saves even when validation finds problems: findings lists them, and errors block publish_workflow, not the save — fix them with update_workflow. saved: false means the graph could not be drawn at all (bad ids, unknown types, broken wiring); nothing was created. Pass automation_id (a UUID you generate) to make retries return the same workflow instead of a duplicate. Nothing runs until publish_workflow and then set_workflow_status live.
update_workflow
Permission:workflows.write · Effect: changes data, destructive or reaches people
Rename a workflow and/or REPLACE its draft. definition is the whole graph, not a patch: read it with get_workflow (draft, or definition when there is no draft), change it, send all of it back. Nodes that keep their id keep their labels. A canvas this API laid out that nobody has rearranged is re-laid out on every save. A canvas a person arranged (moved a node or resized an exit criteria box), or one saved before auto layout existed, keeps its positions and new nodes are fitted around them; pass relayout: true once to tidy such a canvas. Returns the saved workflow and the draft’s findings. The published version keeps running unchanged until publish_workflow. If a person has the workflow open in the builder, their next edit there can overwrite a draft saved here — re-read before publishing.
update_workflow_node
Permission:workflows.write · Effect: changes data, destructive or reaches people
Change ONE node of the draft in place — its params and/or its canvas name and description — without sending the whole graph back. Positions, wiring, unfilled “Add step” nodes and every other node stay exactly as they are (the one exception: changing a drop_off trigger’s params.config re-draws its branch edges). Pass params (the whole new params object) or params_patch (an RFC 7396 merge patch: objects merge key by key, null deletes a key, arrays and values replace), not both. The write only lands on the draft it read: if the draft changes while the edit is being made (a builder save, another write), nothing is saved and you get failed_precondition — re-read and apply it again. Pass expected_draft_hash (draft_hash from get_workflow) to also refuse when the draft changed since YOUR read. saved: false means the node would not be representable on the canvas — nothing was written and findings say why. Otherwise returns the workflow and the draft’s validation findings; errors block publish_workflow, not the save. The published version keeps running unchanged until publish_workflow. Wiring changes (next, adding or removing nodes) still go through update_workflow.
send_test_email
Permission:workflows.publish · Effect: changes data, destructive or reaches people
Send ONE email step of a workflow (send_email / send_marketing_email) as a real email to up to 5 of this organization’s team members — never to anyone else — to check how it reads before publishing. It is rendered exactly as a run would render it: variables are filled from person_id’s data when given (a sample person from search_people), and anything without a value uses the step’s fallback text; fallback_used lists those {{context.*}} paths (with no person, every person variable falls back). The subject is prefixed “[Test] ”. Uses the draft’s version of the step when the draft has it, else the published one (source picks explicitly). No run is created and nothing is recorded as sent, so it never affects who later receives the step. It is a real send through the organization’s sender and counts toward the plan’s email allowance. At most 20 test emails per organization per hour, each recipient counted (resource_exhausted past that).
publish_workflow
Permission:workflows.publish · Effect: changes data, destructive or reaches people
Publish the draft — or definition, which is saved as the draft first — as the workflow’s next version. Full validation runs; any error blocks the publish and comes back in findings (published: false). Pass expected_version (the version you read) so you never publish over someone else’s publish. A LIVE workflow runs the new version on its next trigger; a workflow that is not live still needs set_workflow_status live to run at all.
set_workflow_status
Permission:workflows.publish · Effect: changes data
Turn a workflow live or pause it. Live means its published version runs on every new trigger — real messages to real people — so confirm with the person you are working for first. Requires a published version and an active subscription. Pausing stops new runs.
delete_workflow
Permission:workflows.write · Effect: changes data, destructive or reaches people
Permanently delete a workflow together with its published versions, run history and sent-message history. Cannot be undone. A live workflow is refused — pause it first. To experiment safely, clone_workflow instead.
cancel_run
Permission:workflows.publish · Effect: changes data, destructive or reaches people
Stop one run: an in_progress (or failed) run becomes cancelled and runs no further steps. Steps already dispatched and waits already queued are not chased — each checks the run before it runs and stops. Idempotent: an already cancelled run comes back with changed: false, and a completed run is left completed. Pausing a workflow (set_workflow_status) stops NEW runs; this stops one that is under way. A cancelled run can be resumed with retry_run.
retry_run
Permission:workflows.publish · Effect: changes data, destructive or reaches people
Re-run a failed or cancelled run from a node, as a new attempt, on the workflow version the run originally ran on (not the current draft or published version). from_action_id defaults to where the run stopped (the node of its last error, or the step a cancel interrupted). Steps from the old attempt still in flight are skipped. Steps run asynchronously: this returns once the first step is dispatched — follow it with get_run. Marketing and transactional emails a person already got from a node are not sent again; SMS, webhooks, Slack and Intercom steps are: if any between from_action_id and the end already ran on this run, the call is refused with the list unless confirm_repeat_side_effects is true. Idempotent: a concurrent second retry dispatches nothing (changed: false). An in_progress run that is stuck must be cancel_run’d first.
clone_workflow
Permission:workflows.write · Effect: changes data
Copy a workflow — its draft, or its published canvas when it has no draft — into a new DRAFT workflow. Node ids are regenerated and every reference to them (wiring, exit gates, {{context.<id>}} templates) follows. The copy is never live. Good for trying changes without touching a running workflow.
Webinars
list_webinars
Permission:webinars.read · Effect: read-only
List webinar sessions with filters and paging: status, series, a start-time window. For “what is coming up” pass starts_after = now and order = asc. Returns session summaries; use get_webinar for counts of registrants and messages.
get_webinar
Permission:webinars.read · Effect: read-only
Fetch one webinar session: its schedule, status, platform link, series, registration counts (registered / attended / no-show / unresolved) and its own messages by status. Use get_webinar_plan for what those messages and bound sequences send and when.
create_webinar
Permission:webinars.write · Effect: changes data
Create a one-off webinar session. Pass webinar_id (a v4 UUID you generate) to make it safe to retry: the same id returns the session already made (created: false). For a recurring webinar use create_webinar_series instead. Then link it to its platform (link_webinar_platform) and check what registrants will receive with get_webinar_plan.
update_webinar
Permission:webinars.write · Effect: changes data, destructive or reaches people
Change a webinar session. Omitted fields stay as they are; null clears timezone, duration_minutes, join_url or automation_id. A new start_time (a reschedule) or duration_minutes moves every pending reminder, the session’s own messages and the platform attendance pull with it — the response says what moved. status moves: draft → scheduled | canceled; scheduled → live | completed | canceled; live → completed | canceled; canceled → scheduled (restore; messages whose time passed while canceled are not re-sent); completed is final. Canceling stops the session’s own messages and refuses new registrations and attendance; sequence and workflow runs registrants are already in keep going. Linked platforms are not rescheduled — change the time on the platform too. Confirm with the person you work for before canceling or rescheduling a session people registered for.
delete_webinar
Permission:webinars.write · Effect: changes data, destructive or reaches people
Delete a webinar session that never gathered anything: no registrations, no messages sent, not part of a series and not a series’ template. Anything else is refused with the reason — cancel it instead (update_webinar status canceled), which keeps the history; for a series session, cancel it or skip its date on the series.
clone_webinar
Permission:webinars.write · Effect: changes data
Copy a webinar session to a new start time: title, duration, join link and its messages (as drafts, re-anchored to the new start — nothing sends until scheduled). Registrations and the platform link are not copied; link the clone with link_webinar_platform. Pass new_webinar_id (a v4 UUID) to make a retry safe.
list_webinar_registrants
Permission:webinars.read · Effect: read-only
Who registered for a webinar session, with what the session knows about each: attended or not, watch time where the platform reports it, left early, offer clicks, purchase and refund. segment narrows to one audience; segment_counts always covers everyone.
register_for_webinar
Permission:webinars.write · Effect: changes data, destructive or reaches people
Register a person for a webinar session. This sends real messages: it enrolls them into every live workflow and sequence bound to the session (confirmations, reminders) and pushes them to the linked platform for their join link. Resolve the person first (search_people, or identify_person to create them). Returns join_url, the link this person joins with: their own when the platform issued one (join_url_personal: true), otherwise the session’s shared link. Idempotent: registering someone twice changes nothing and sends nothing (already_registered: true, with the same join_url). A canceled session refuses registrations. Confirm with the person you work for before registering real people.
set_webinar_attendance
Permission:webinars.write · Effect: changes data, destructive or reaches people
Record whether a registered person attended a webinar session (attended: false marks a no-show). This arms the session’s outcome follow-ups — the attended and no-show sequences — so it sends real messages after the session. Linked platforms (Demio, WebinarJam, Zoom) report attendance on their own; use this for sessions without one, or to correct a record.
get_webinar_plan
Permission:webinars.read · Effect: read-only
Everything a webinar session sends and when: its own clock-anchored messages, the workflows and sequences bound to it (registration and outcome rails), and — for each entry time — the exact, timestamped list someone registering at that moment receives, with late steps skipped as the runtime skips them. With no entry_times it samples ten days out, three days out, two hours out and after the end. Check this before and after changing a session’s messages or schedule.
get_webinar_revenue
Permission:webinars.read · Effect: read-only
What a webinar session earned: registered and attended counts, clicks, buyers, and revenue split into total, attributed to its messages, and unattributed — with a per-message breakdown. reconciles: false means the figures do not add up; report that rather than the numbers.
link_webinar_platform
Permission:webinars.write · Effect: changes data
Link a webinar session to the platform it runs on, so registrations are pushed there (personal join links) and attendance comes back after it ends. demio: event_id is the Demio event, date_id the session inside it; by default the session’s title, start, duration, timezone and registration link are pulled from Demio (sync_details: false keeps yours) — a moved start re-anchors reminders. webinarjam: event_id is the WebinarJam webinar id, date_id its schedule id — leave it out and the schedule on the session’s date is picked; the operator must also add the registration / attended / missed automation rules on the WebinarJam side. zoom: event_id is the Zoom webinar id (spaces fine); it must exist on the connected Zoom account, and a one-off Zoom webinar can back only one session. Linking a series’ template session to a recurring Zoom webinar (recurring: true) also links every upcoming session of that series to it, and each session the series builds from then on. The response names what was linked (topic) so you can confirm it. Demio and Zoom must be connected in Settings → Integrations; WebinarJam links without an API key but then cannot pick the schedule or push registrations. A recurring WebinarJam webinar shares one schedule id across every date, so it cannot back a series: use one WebinarJam schedule per date.
unlink_webinar_platform
Permission:webinars.write · Effect: changes data, destructive or reaches people
Disconnect a webinar session from its platform (Demio, WebinarJam or Zoom): new registrations stop being pushed there and the post-session attendance pull is cancelled. Registrations and attendance already recorded stay. Unlinking a series’ template session from Zoom also unlinks the series’ upcoming sessions that shared that Zoom webinar.
list_webinar_series
Permission:webinars.read · Effect: read-only
List webinar series — repeat rules that keep sessions built ahead on a cadence — with cadence, status and template session.
get_webinar_series
Permission:webinars.read · Effect: read-only
Fetch one webinar series: cadence, the sessions it has built, skipped dates, and upcoming_unbuilt — the next dates it has not built yet, which is what skip_webinar_occurrence takes.
create_webinar_series
Permission:webinars.write · Effect: changes data
Create a recurring webinar: the first session (on the first cadence slot after now) becomes the template, and the series keeps build_ahead_count sessions built ahead, each a copy of the template (title, duration, join link, messages — armed messages stay armed). Set up the template session’s messages, then new sessions inherit them — sessions built during this call copy the template as it is now (empty), so keep build_ahead_count low until the template is ready, or set those sessions’ messages up too. The platform link is inherited only where one link can serve every date: link the template to a recurring Zoom webinar and every upcoming session of the series is linked to it, including the ones already built; with WebinarJam each session is linked to the schedule on its own date; a one-off Zoom webinar or a Demio event stays the template’s alone, so link those sessions one by one. Pass series_id (a v4 UUID) to make a retry safe.
update_webinar_series
Permission:webinars.write · Effect: changes data, destructive or reaches people
Change a webinar series’ rule — name, cadence, timezone, build_ahead_count, or status (active builds; paused and archived build nothing). Applies to sessions not built yet; existing sessions keep their times (reschedule one with update_webinar). What each new session copies comes from the template session, so edit that session to change future ones. The series re-checks its horizon immediately.
materialize_webinar_series
Permission:webinars.write · Effect: changes data
Build any sessions a webinar series is short of right now, instead of at its next scheduled check. Safe to repeat — it only fills gaps.
skip_webinar_occurrence
Permission:webinars.write · Effect: changes data
Stop a webinar series from building one upcoming date (take it from get_webinar_series upcoming_unbuilt). A date that already has a session is not skipped here — cancel that session with update_webinar instead.
unskip_webinar_occurrence
Permission:webinars.write · Effect: changes data
Put a skipped date back on a webinar series. If it falls inside the build-ahead horizon its session is built immediately.
get_webinar_series_revenue
Permission:webinars.read · Effect: read-only
What a webinar series earned across its sessions, deduplicated (one purchase attributed to two sessions counts once), with a per-session breakdown of the most recent sessions. The per-session figures do not sum to the series total by design.
Broadcasts
list_broadcasts
Permission:broadcasts.read · Effect: read-only
List broadcasts, newest first, with filters and paging: status (draft | scheduled | sending | sent | stopped | error), channel (email | sms), webinar_id. Returns summaries with sent / opened / clicked counts; get_broadcast has the body.
get_broadcast
Permission:broadcasts.read · Effect: read-only
Fetch one broadcast: subject, preview text and HTML body (or the text), its variables, audience, timing, sender and counts — in the form update_broadcast takes, so edit and send back.
list_broadcast_variables
Permission:broadcasts.read · Effect: read-only
What a broadcast’s variables can read: the recipient’s fields, the organization’s custom properties, and — for a broadcast tied to a webinar — the session (title, start time, the recipient’s join link, calendar links). Each entry’s variable goes straight into a variable definition.
No parameters.
create_broadcast
Permission:broadcasts.write · Effect: changes data
Create a broadcast draft. Nothing is sent: next plan_broadcast, then schedule_broadcast. audience is required: everyone (every person with an email address), segment (with segment_id), or webinar (with webinar_id — the session’s registrants, narrowed by webinar_audience, optionally timed from the session with webinar_offset_minutes). Email needs subject, html and sender_id before it can be scheduled; a text broadcast (channel sms, webinars only) needs message_body. Personalize with variables (list_broadcast_variables). Pass broadcast_id (a v4 UUID you generate) to make the call safe to retry.
update_broadcast
Permission:broadcasts.write · Effect: changes data
Change a broadcast draft. Omitted fields stay as they are; null clears preview_text, sender_id or webinar_offset_minutes. html / message_body replace the whole body (read it with get_broadcast first); variables replace the whole variable map — the body and its variables are checked together. Within the same audience kind, stored ids and timing are kept unless you send new ones; switching kind needs the new kind’s id. channel cannot be changed. A scheduled broadcast has to be unscheduled first; a stopped or errored one returns to draft. Any change invalidates an earlier plan — plan again.
delete_broadcast
Permission:broadcasts.write · Effect: changes data, destructive or reaches people
Delete a broadcast nobody has received any of (a draft, or a stopped / errored one that never went out). A scheduled one has to be unscheduled first; one that went out stays for its history.
plan_broadcast
Permission:broadcasts.read · Effect: read-only
Show exactly what scheduling a broadcast would do, without scheduling it: how many people it reaches (counted the way the sender counts), when it goes out and how long it takes, who it is from, the body as one real recipient would get it, and checks — errors block, warnings are worth a look. Pass send_at (ISO-8601 with an offset, at least an hour from now; plan 90+ minutes out so the plan survives review, since the hour is checked again at scheduling). Omit send_at for a webinar broadcast timed from its session (webinar_offset_minutes set): passing one is an error. When nothing blocks, returns a plan_token for schedule_broadcast (good for an hour, and only while the broadcast stays as planned). Show the plan to the person you work for before scheduling.
schedule_broadcast
Permission:broadcasts.send · Effect: changes data, destructive or reaches people
Schedule a broadcast to go out to real people, as planned: pass the plan_token from plan_broadcast. It goes out at the planned time (never sooner than an hour from now) and is refused if anything changed since the plan, the plan is over an hour old, or the send time is now less than an hour away — plan again then. The plan’s checks also run again: anything that now blocks (sender domain unverified, session canceled or started, allowance used up) refuses it, and edits to the target segment’s filters or a rescheduled session count as changes. Only call this after the person you work for has seen the plan and agreed. Until it goes out, unschedule_broadcast (or Unschedule in the dashboard) pulls it back.
unschedule_broadcast
Permission:broadcasts.write · Effect: changes data
Take a scheduled broadcast back to draft so nothing goes out. Refused once sending has begun.
get_broadcast_stats
Permission:broadcasts.read · Effect: read-only
How a broadcast did: sent / opened / clicked totals and per day (email), and where every queued recipient ended up — sent, still queued, failed, or skipped (and why: opted out, bounced, complained, duplicate, allowance used up, webinar already started).
senders
list_senders
Permission:senders.read · Effect: read-only
The email senders a broadcast or workflow email can go out from (use the id as sender_id / sender_config_id) and whether each is ready — a sender needs a verified sending domain. team_member marks a teammate’s own sender (made when they joined) as opposed to an organization sender. Also whether the organization has an SMS number for text broadcasts.
No parameters.
list_sending_domains
Permission:senders.read · Effect: read-only
The organization’s sending domains — what can follow the @ in a sender’s address. ready = verified, so create_sender can use it; an unverified domain lists the DNS records still missing (adding a domain and verifying it happens in the dashboard, Settings → Email). sender_count is how many senders use each.
No parameters.
create_sender
Permission:senders.write · Effect: changes data
Create an organization email sender: from_name plus <username>@<domain> on one of the organization’s verified sending domains (list_sending_domains). Nothing is sent — use the returned id as a broadcast’s sender_id or a workflow email’s sender_config_id. Refused for a domain that is not this organization’s or not verified yet. Safe to retry: if a sender already sends from that address it is returned unchanged (created: false; change it with update_sender), and a sender_id you generate makes the call idempotent.
update_sender
Permission:senders.write · Effect: changes data, destructive or reaches people
Change a sender. Omitted fields stay as they are; reply_to null = replies go to the from address. A new username or domain_id is checked like create_sender (own verified domain, valid username) and refused if another sender already uses the resulting address. Takes effect at the next send everywhere the sender is used: the result counts the workflows and sequences (live, or with runs still in progress), scheduled broadcasts and published transactional templates that send from it and lists warnings (agents that name the sender are not counted) — tell the person you are working for before changing a sender that is in use. A sender cannot move to another team member or organization.
Signals
list_signal_rules
Permission:signals.read · Effect: read-only
List the event rules: each turns a matching event into a signal (a person flagged for action), with its routing (human or agent, rung), dedupe, expiry, resolve_on and follow_up.
No parameters.
get_signal_rule
Permission:signals.read · Effect: read-only
Fetch one event rule by id.
create_signal_rule
Permission:signals.write · Effect: changes data
Create an event rule, or replace one whole when key matches an existing rule (safe to retry). Takes effect within a minute. A rule with handler agent and rung auto starts that agent on every new signal (capped per agent per day); every new signal also emits $fw_signal_created for playbook workflows. To change one field of an existing rule use update_signal_rule.
update_signal_rule
Permission:signals.write · Effect: changes data
Change some fields of an event rule. Omitted fields keep their value; null clears any nullable field (follow_up, handler_agent_id, occurrence, key, label, default_assignee_user_id, contact_reason). The merged rule is re-validated whole.
delete_signal_rule
Permission:signals.write · Effect: changes data, destructive or reaches people
Delete an event rule. Signals it already wrote keep their history.
list_review_triggers
Permission:signals.read · Effect: read-only
List the triggers that summon the AI reviewer to look at a person when an event happens.
No parameters.
create_review_trigger
Permission:signals.write · Effect: changes data
Summon the AI reviewer when an event (plus filters, optional occurrence count) happens, after a delay, optionally with a focus question.
update_review_trigger
Permission:signals.write · Effect: changes data
Change some fields of a review trigger. Omitted fields keep their value.
delete_review_trigger
Permission:signals.write · Effect: changes data, destructive or reaches people
Delete a review trigger.
list_signal_actions
Permission:signals.read · Effect: read-only
List the AI reviewer’s action catalog: the verbs it may answer with (human_reachout, agent_draft, alert, watch, none, plus custom ones) and how each one’s signals are routed.
No parameters.
create_signal_action
Permission:signals.write · Effect: changes data
Add a verb to the reviewer’s action catalog. rung_ceiling is fixed at creation and can never be raised.
update_signal_action
Permission:signals.write · Effect: changes data
Change how a reviewer action is routed or guided: e.g. route agent_draft to an agent at rung auto. Omitted fields keep their value. rung cannot exceed the action’s rung_ceiling.
delete_signal_action
Permission:signals.write · Effect: changes data, destructive or reaches people
Delete a verb from the reviewer’s catalog. Permanent: a deleted shipped verb is not re-seeded.
list_event_dictionary
Permission:signals.read · Effect: read-only
What each event means to the AI reviewer: description, salience (critical | notable | routine | noise, which decides what the reviewer sees), category, expected_next, and whether it counts as activity.
update_event_dictionary
Permission:signals.write · Effect: changes data
Classify an event the org has already sent, as an operator: salience, category, description, expected_next. Operator edits outrank AI and stats classification. Only events the org has already sent (anything else is not found). Salience critical books a review on every occurrence while the reviewer is on; noise removes the event from the reviewer’s dossier.
get_reviewer_settings
Permission:signals.read · Effect: read-only
The AI reviewer’s settings (enabled, scan segment, org brief, debounce, staleness, max findings) and where the org stands against its review allowance this period.
No parameters.
update_reviewer_settings
Permission:signals.write · Effect: changes data
Change the AI reviewer’s settings. Omitted fields keep their value. scan_segment_id is stored and checked to be one of the org’s segments, but the scan sweep does not apply it yet. Switching the reviewer on needs the AI Reviewer on the plan; check the allowance first, it spends money.
list_signal_agents
Permission:signals.read · Effect: read-only
The org’s agents that signals can be routed to, with each one’s daily dispatch cap and how many signals it has been handed today (UTC).
No parameters.
list_signal_producers
Permission:signals.read · Effect: read-only
The deterministic signal detectors (event_rules, webinar_session_ended) and whether each is active or paused.
No parameters.
set_signal_producer_status
Permission:signals.write · Effect: changes data
Pause or resume a signal detector. A paused detector writes no signals.
agents
set_signal_agent_cap
Permission:agents.write · Effect: changes data
Set how many signals one agent may be handed per UTC day (first dispatches and follow-ups together). Default 25. 0 stops dispatch to it. Signals past the cap wait for the next UTC day rather than being dropped.
Documentation
search_docs
Permission:none (public) · Effect: read-only
Search Flywheel’s documentation and this server’s tool catalog. Returns the best-matching doc sections (with the page path to pass to get_doc) and tools (with the permission each needs). Use it before guessing how something works: workflow definition format and node params, validation finding codes, segment filters, broadcast variables and planning, webinar series, signals, connecting and permissions. Ask in plain words, e.g. ‘if/else branch on a custom property’ or ‘why was my broadcast plan refused’.
get_doc
Permission:none (public) · Effect: read-only
Read one Flywheel documentation page as markdown — the whole page, or one section of it (a ## section includes its ### subsections). Paths come from search_docs results (e.g. ‘mcp/workflow-definitions’); a full docs URL works too. Start with ‘mcp/overview’ for the map of what the MCP can do and which page covers what.
list_docs
Permission:none (public) · Effect: read-only
The table of contents of Flywheel’s documentation: every page’s path, title, one-line description and section headings, grouped as on the docs site. Use it to browse when search_docs does not find what you need; read a page with get_doc.