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

# MCP Integration

> Connect Sequenzy to ChatGPT, Claude.ai, Claude Desktop, Codex, Cursor, and other MCP clients using the Model Context Protocol

# MCP Integration

Sequenzy supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.io), allowing AI assistants like ChatGPT, Claude.ai, Claude Desktop, Claude Code, Codex, Cursor, and other MCP clients to manage your email marketing directly.

<Warning>
  Sequenzy is not fully agent-friendly yet. MCP, CLI, and `auth.md` discovery
  cover many common workflows, but some setup, billing, destructive account
  actions, advanced configuration, and final send decisions may still require
  dashboard use or explicit human review.
</Warning>

## What You Can Do

With Sequenzy MCP, AI assistants can:

* **Manage subscribers** - Add, update, search, and remove subscribers
* **Manage your audience** - Create, update, and delete lists, segments, and tags, and enroll subscribers in sequences
* **Create sequences** - Generate and configure email automation sequences
* **Draft campaigns** - Create and edit campaign drafts
* **Manage landing pages** - Create, edit, publish, unpublish, and connect custom domains
* **Build signup forms** - Create list-scoped saved forms and return client-safe static-site embed code
* **Control campaign delivery** - Cancel, pause, resume, duplicate, and delete campaigns, and run campaign A/B tests
* **Send transactional emails** - Send single emails via template or HTML
* **Manage your team** - List members, invite teammates, and cancel invitations
* **Triage your inbox** - Read, reply to, and close subscriber conversations
* **Manage webhooks** - Create, test, and replay outbound webhook deliveries
* **View analytics** - Get stats for campaigns, sequences, and overall performance
* **Manage localization** - Read, supply, and AI-sync localized template variants
* **Generate content** - AI-powered email and sequence generation

## Remote Setup For ChatGPT, Claude.ai, And Mobile

Use the remote connector when you want Sequenzy tools in ChatGPT, Claude.ai, Claude mobile, and other cloud-hosted MCP clients that cannot launch a local `npx` process.

You can copy the connection details from the existing MCP button in the Sequenzy dashboard sidebar:

1. Open your workspace dashboard
2. Click the **MCP** button in the sidebar developer setup area
3. Copy the **Remote MCP server URL**
4. Paste it into your AI client's custom connector settings
5. Complete the Sequenzy OAuth approval flow

Remote MCP server URL:

```text theme={null}
https://api.sequenzy.com/v1/mcp
```

In Claude:

1. Open **Settings → Connectors**
2. Choose **Add custom connector**
3. Enter a name such as `Sequenzy`
4. Enter the remote MCP server URL above
5. Complete the Sequenzy OAuth login flow

After connecting, Claude sends authenticated MCP requests to Sequenzy from Anthropic's cloud infrastructure, so the connector is available from Claude web and mobile without keeping Claude Desktop open. Remote setup uses OAuth; you do not need to paste a Sequenzy API key into Claude.

In ChatGPT:

1. Open **Settings → Connectors** (custom connectors require a plan with
   connector support and **Developer mode** enabled under **Advanced settings**)
2. Choose **Create** to add a custom connector
3. Enter a name such as `Sequenzy`
4. Enter the remote MCP server URL above
5. Complete the Sequenzy OAuth login flow
6. In a new chat, enable the Sequenzy connector from the composer's tools menu

Like Claude, ChatGPT uses OAuth for the remote connector; you do not need to paste a Sequenzy API key into ChatGPT.

Agents that support user-approved service registration can also discover the current flow through [`auth.md`](https://www.sequenzy.com/auth.md). For broader agent setup notes, including the distinction between API/MCP auth and dashboard signup, read [`agents.md`](https://www.sequenzy.com/agents.md).

## Hand Off from the Dashboard

The AI assistant sidebars in the sequence builder and the email editor include
an **Open in** menu next to the message box. Type what you want to change,
then:

* **Copy prompt** copies an agent-ready prompt that includes your company ID
  and the current sequence or campaign, for use with any agent
* **Open in ChatGPT** or **Open in Claude** opens the prompt in a new chat
* **Open in Cursor** opens the prompt in the Cursor desktop app
* **Set up Sequenzy MCP** opens the AI Agent Setup wizard if your agent is not
  connected yet

Agents connected through the Sequenzy MCP server can use the IDs in the prompt
to inspect and edit the exact resource you were working on.

The prompt is self-sufficient: it names your workspace and ends with the two
ways an unconnected agent can get access - add the remote MCP server URL in
its connector settings (with a link to the setup guides on this page), or use
an API key you create in the dashboard under **Settings → API Keys** against
the REST API. The prompt itself never contains an API key: it is copied
through the clipboard and hosted-agent URLs, so you paste the key to the
agent directly instead.

## Local Setup

Use local setup for desktop and IDE clients that support local stdio MCP servers, including Claude Desktop, Claude Code, Codex, Cursor, Windsurf, and VS Code.

You can also use the sidebar **MCP** button in the Sequenzy dashboard to copy the local setup command, create a personal key for manual local configs, and jump to the full setup docs.

For MCP authorization and generated local MCP keys, **Safer agent access** is the default. It lets the agent inspect data and draft content without live delivery, destructive deletes, team changes, or API key creation. Choose **Full access** when you intentionally want the agent to use the complete MCP surface, including sending, deletes, and administration. Use sender presets or custom scopes when the agent should send only specific kinds of email.

### Quick Setup

The easiest way to set up MCP is using our setup wizard:

```bash theme={null}
npx @sequenzy/setup
```

This will:

1. Open your browser to log in
2. Create a personal API key
3. Auto-detect supported installed AI clients, including Codex, Claude Code, Claude Desktop, Cursor, Windsurf, and VS Code
4. Configure those clients automatically using their native MCP CLI when available, with config-file fallback when needed

### Manual Setup

Across most MCP clients, the core server config is the same:

* Command: `npx`
* Args: `-y @sequenzy/mcp`
* Env: `SEQUENZY_API_KEY=seq_user_your_key_here`

If your client supports local stdio MCP servers, you can usually reuse that exact command and environment variable in its native config format.

#### Claude Desktop

1. Open Claude Desktop settings file:
   * **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
   * **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

2. Add Sequenzy MCP:

```json theme={null}
{
  "mcpServers": {
    "sequenzy": {
      "command": "npx",
      "args": ["-y", "@sequenzy/mcp"],
      "env": {
        "SEQUENZY_API_KEY": "seq_user_your_key_here"
      }
    }
  }
}
```

3. Restart Claude Desktop

#### Cursor

1. Open `~/.cursor/mcp.json` (create if it doesn't exist)

2. Add Sequenzy:

```json theme={null}
{
  "mcpServers": {
    "sequenzy": {
      "command": "npx",
      "args": ["-y", "@sequenzy/mcp"],
      "env": {
        "SEQUENZY_API_KEY": "seq_user_your_key_here"
      }
    }
  }
}
```

3. Restart Cursor

#### Windsurf

Same format as Cursor, but the config is at:

* **macOS**: `~/Library/Application Support/Windsurf/mcp.json`
* **Windows**: `%APPDATA%\Windsurf\mcp.json`

#### Claude Code

You can add Sequenzy to Claude Code directly from the CLI:

```bash theme={null}
claude mcp add --scope user --env=SEQUENZY_API_KEY=seq_user_your_key_here sequenzy -- npx -y @sequenzy/mcp
```

If you prefer a shared project config, add the same server to `.mcp.json`:

```json theme={null}
{
  "mcpServers": {
    "sequenzy": {
      "command": "npx",
      "args": ["-y", "@sequenzy/mcp"],
      "env": {
        "SEQUENZY_API_KEY": "seq_user_your_key_here"
      }
    }
  }
}
```

On native Windows, wrap `npx` with `cmd /c`:

```bash theme={null}
claude mcp add --scope user --env=SEQUENZY_API_KEY=seq_user_your_key_here sequenzy -- cmd /c npx -y @sequenzy/mcp
```

#### Codex

You can add Sequenzy to Codex from the CLI:

```bash theme={null}
codex mcp add sequenzy --env SEQUENZY_API_KEY=seq_user_your_key_here -- npx -y @sequenzy/mcp
```

Verify that Codex can see the server:

```bash theme={null}
codex mcp list
```

Or add it manually in `~/.codex/config.toml`:

```toml theme={null}
[mcp_servers.sequenzy]
command = "npx"
args = ["-y", "@sequenzy/mcp"]

[mcp_servers.sequenzy.env]
SEQUENZY_API_KEY = "seq_user_your_key_here"
```

#### VS Code Copilot

VS Code uses a `servers` object in its MCP config:

```json theme={null}
{
  "servers": {
    "sequenzy": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@sequenzy/mcp"],
      "env": {
        "SEQUENZY_API_KEY": "seq_user_your_key_here"
      }
    }
  }
}
```

#### Other MCP Clients

For tools like OpenClaw, Hermes, and other MCP-compatible clients, use the same Sequenzy stdio server:

* Command: `npx`
* Args: `-y @sequenzy/mcp`
* Env: `SEQUENZY_API_KEY=seq_user_your_key_here`

If a client asks for a JSON config, use the same `mcpServers.sequenzy` shape shown above unless its docs specify a different schema. If it provides a CLI helper, point it at `npx -y @sequenzy/mcp` and set the same environment variable.

## Getting Your API Key

1. Go to [the Sequenzy dashboard](https://sequenzy.com/dashboard)
2. Click the **MCP** button to create a personal key for local MCP, or open **Settings → API Keys** for backend keys
3. Choose a permission preset or custom permissions
4. Copy the key (personal keys start with `seq_user_`)

Or use the setup wizard: `npx @sequenzy/setup`

### Recover from missing API key permissions

When a tool reports a missing scope such as `campaigns:read` or
`templates:write`, call `get_account` first. Its `apiKeyPermissions` field shows
the current key's `activeKey` identity, effective preset, full-access state,
scope counts and description, exact scopes, common missing marketing read
scopes, and a direct `manageUrl` for the selected company's API Keys settings.
Compare `activeKey.name` and the non-secret `activeKey.prefix`
with the key listed in Settings to confirm that the MCP client actually loaded
your replacement credential. Each company also includes a `settingsUrl`. If the
key does not include `account:read`,
`get_account` cannot return that metadata; open the
[Sequenzy dashboard](https://sequenzy.com/dashboard) directly and use the MCP
setup or **Settings → API Keys** instead.

API key permissions cannot be changed after creation.

For a local API-key connection, open `manageUrl` (or
`companies[].settingsUrl` and select **API Keys**), then:

1. Choose **Read-only** for discovery-only access, **Safer agent access** for
   common drafting and setup work, or **Custom** and include every scope named
   in the error.
2. Replace `SEQUENZY_API_KEY` in the MCP configuration.
3. Restart the MCP client so it uses the replacement key.

For hosted OAuth MCP, disconnect the Sequenzy connection and reauthorize it.
Choose a preset or custom permissions that include every scope named in the
error, then reconnect.

## Available Tools

### Subscribers

* `add_subscriber` - Add a new subscriber; its status is creation-only, so use `update_subscriber` for an existing contact
* `create_subscriber_import` - Queue up to 5,000 full CRM subscriber records in one call, including names, external IDs, phone numbers, tags, lists, statuses, and custom attributes
* `get_subscriber_import` - Check import progress and row-level outcome counts
* `update_subscriber` - Update profile fields, attributes, tags, or global status; `status: "unsubscribed"` suppresses future marketing while preserving history
* `remove_subscriber` - Unsubscribe while preserving history, or permanently delete only with `hardDelete: true`
* `get_subscriber` - Get subscriber details
* `search_subscribers` - Search by tags, lists, attributes, segments
* `trigger_subscriber_event` - Emit a custom event for one subscriber exactly as an integration would. This is the supported way to exercise event triggers, matching-field idempotency, branch conditions, and stop conditions end to end without waiting for real traffic
* `trigger_subscriber_events` - Emit several events for one subscriber in order
* `bulk_add_subscriber_tags` / `bulk_remove_subscriber_tags` - Add or remove tags across up to 500 existing subscribers identified by `emails`, `externalIds`, or `subscriberIds`. Built for reconciling derived-tag backlogs: unknown identifiers come back in `notFound` instead of creating contacts, and tag automations stay off unless you pass `triggerAutomations: true`

### Lists

* `list_lists` - List subscriber lists
* `create_list` - Create a subscriber list
* `update_list` - Update a list's name, description, or privacy
* `delete_list` - Permanently delete a list and all of its memberships; subscribers themselves are kept
* `add_subscribers_to_list` - Bulk add up to 500 subscribers to a list from an email array
* `remove_subscribers_from_list` - Remove up to 500 subscribers from a list by email; subscribers stay in your audience and unmatched emails are returned in `notFound`

### Campaigns

* `list_campaigns` - List all campaigns, including reviewer feedback in `rejectionComment` for rejected campaigns
* `get_campaign` - Get campaign details and stats, including the saved audience in `targetLists` (null when targeting is still unset) and reviewer feedback in `rejectionComment` for rejected campaigns. Note that `computedLists` is email personalization (product lists rendered inside the email), not audience targeting
* `get_campaign_audience` - Resolve exactly who a campaign will reach: targeting kind, resolved list and segment names (with a `missing` flag for deleted references), filters, include/exclude rules, a plain-language summary, and a live recipient count. `isUnset: true` means scheduling would send to every active subscriber
* `create_campaign` - Create a campaign from a prompt, raw HTML, or Sequenzy blocks; native blocks include [Poll and NPS surveys](/concepts/polls). Prompt generation saves the generated subject, preview text, company logo/footer, and uses the company font. Defaults to draft, with `status: "sent"` available for archived/imported sent campaigns. Pass `targetLists` (or the `segmentId` shorthand) to save the audience on the draft, or omit both and choose it in `update_campaign` or `schedule_campaign`
* `update_campaign` - Edit a draft, including reply-to settings, raw HTML, or Sequenzy blocks such as Poll and NPS surveys. Accepts `targetLists` (or the `segmentId` shorthand) to retarget the draft, or `targetLists: null` to clear saved targeting
* `schedule_campaign` - Schedule a draft or already scheduled campaign; pass `recurringInterval: "weekly"` or `"monthly"` to repeat it automatically, re-evaluating audience membership at every run
* `unschedule_campaign` - Remove a scheduled send and recurrence, returning the campaign to an editable draft that can be scheduled again
* `send_test_email` - Send test to single address
* `render_email` - Render a campaign, sequence email step, or template to the exact email-safe HTML that would be sent, for embedding a visual preview in an external dashboard or builder. Pass exactly one of `campaignId`, `sequenceId` plus `nodeId`, or `templateId`. Read-only: it never sends or modifies anything
* `cancel_campaign` - Cancel a scheduled, paused, or sending campaign; remaining emails are not sent and the campaign cannot be restarted
* `pause_campaign` - Pause a campaign that is currently sending
* `resume_campaign` - Resume a paused campaign, optionally spreading the remaining delivery over 1-72 hours with `spreadOverHours`
* `duplicate_campaign` - Duplicate a campaign as a new draft. `mode: "campaign"` (default) copies the campaign and its email, `"ab_test"` also copies the A/B test with all variants, and `"variant"` copies a single variant's content (requires `variantId`)
* `resend_campaign_to_non_openers` - Create a draft that resends a sent campaign to everyone in the same audience who didn't open it. Available 6 hours after the campaign finishes sending; returns the draft and an estimate of how many subscribers haven't opened the original. The draft must be scheduled or sent separately
* `delete_campaign` - Permanently delete a campaign; sending, scheduled, or paused campaigns must be cancelled with `cancel_campaign` first

### Saved Forms

* `list_forms` - List saved forms, audience settings, and public action URLs
* `create_form` - Create and publish a form scoped to one or more list IDs
* `get_form_embed` - Get the action URL, hosted JavaScript, native form, and fetch example

For a static Astro, Hugo, Jekyll, Cloudflare Pages, Netlify, or GitHub Pages
site, start with `list_forms`, call `create_form` if needed, then use
`get_form_embed`. The returned browser code never includes a Sequenzy API key;
the opaque form ID selects the server-managed lists, tags, duplicate behavior,
and success action.

### Landing Pages

* `list_landing_pages` - List landing pages with status, metrics, content, and URLs
* `get_landing_page` - Get landing page details, editor content, metrics, and public URLs
* `create_landing_page` - Create a draft landing page from default template content or supplied JSON
* `update_landing_page` - Edit landing page name, slug, or full editor-compatible content
* `publish_landing_page` - Publish a landing page, optionally saving name, slug, or content first
* `unpublish_landing_page` - Return a landing page to draft, optionally saving edits first
* `delete_landing_page` - Delete a landing page
* `connect_landing_page_domain` - Connect a custom landing page domain and return DNS setup details
* `update_landing_page_domain_settings` - Replace or verify landing page custom domain settings

Landing page content uses Sequenzy's editor-compatible JSON schema with `version`, `template`, `seo`, `theme`, and `blocks`. The API validates CTA, pricing, footer, and form redirect URLs before saving or publishing. Custom landing page domains require a CNAME record pointing to `pages.sequenzydns.com`; call `update_landing_page_domain_settings` with `verify: true` after DNS changes propagate.

### Company

* `list_api_keys` - List company API keys as non-secret metadata, including IDs, prefixes, permission receipts, usage timestamps, and the active-key marker
* `revoke_api_key` - Permanently revoke an exact company API key by ID after checking it with `list_api_keys` (`delete_api_key` is a compatibility alias)
* `get_company` - Get company details, product info, brand colors, AI writing context, and effective localization settings
* `update_company` - Edit product info, brand context, and the default email theme (`emailTheme` accepts partial updates; `null` resets it), or set account-wide `fromEmail` and `replyTo` defaults (From domains must be verified); send `fromName` or `replyToName` alone to rename the existing default profile's display name, or pair them with `senderProfileId` / `replyProfileId` from `list_sender_profiles` to pick and rename a specific profile
* `add_sending_domain` - Add a sending domain and return the SPF, DKIM, MAIL FROM, and inbound DNS records required for setup (`add_website` remains as a compatibility alias)
* `list_websites` / `check_website` - Read stored aggregate, SPF, DKIM, and MAIL FROM status
* `verify_sending_domain` - Run a fresh sending-domain verification and return current diagnostics
* `get_sync_rules` - Get the automatic tag changes applied when events fire, plus whether the optional platform preset is active
* `update_sync_rules` - Replace the full sync rule set (`[]` disables rules; `null` opts into the inherited SaaS/ecommerce preset); rules support subscriber-tag conditions and a product match (tags, collections, product types, vendors) for commerce events
* `get_shopify_automation_settings` - Get the connected Shopify store's browse-abandonment, cart-abandonment, and price-drop settings with defaults applied
* `update_shopify_automation_settings` - Update browse-abandonment, cart-abandonment, and/or price-drop settings (partial update; `null` resets a section to the defaults)
* `list_integrations` - List connected integrations (Stripe, Shopify, Supabase, Clerk, ad platforms, and so on) with connection state, sync health, last sync time, and last sync error. Credentials, access tokens, and webhook secrets are never returned
* `list_sender_profiles` - List sender (From) and reply-to profiles, which are the account defaults, and whether each sender address sits on a verified sending domain
* `get_tracking_settings` - Get open/click/unsubscribe tracking flags, the default attribution window, automatic UTM tagging, the dedicated click-tracking domain and its status, and inbound reply-tracking settings

For a new sending domain, call `add_sending_domain`, publish the records in `website.dnsRecords`, wait for DNS propagation, and then call `verify_sending_domain`. Checking or verifying an unconfigured domain returns a recovery message that points back to `add_sending_domain`.

### Sequences

* `list_sequences` - List sequences with dashboard-compatible status, search, label, limit, and offset filters. Branch on `effectiveStatus` (`draft`, `live`, `enrollment_paused`, `paused`, `archived`) rather than `status`, which reads `active` even when new enrollments are paused; `acceptsNewEnrollments`, `processesExistingEnrollments`, and the plain-language `effectiveStatusSummary` are returned alongside it. The legacy `triggerConfig.active` flag is not read by the runtime and now mirrors `acceptsNewEnrollments`
* `get_sequence` - Get sequence details plus normalized `sequence.nodes` with each node's `id`, `nodeType`, current `config`, `updatedAt`, and `updateHints` describing editable/managed fields and the concurrency token to return; reusable `sequence.edges`, `graphRevision`, and editable `sequence.emails` are also returned, including each linked email's effective `emailPreset` (`branded` or `minimal`)
* `send_sequence_test_email` - Send one saved email step to 1-10 internal reviewers without enabling the sequence or enrolling subscribers. Pass the `sequenceId` and email-step `nodeId` from `get_sequence`; each result includes a durable `emailSendId` for `get_email_send`
* `create_sequence` - Create a sequence with:
  * Only `name` for a blank, disabled trigger-to-completion draft matching the dashboard; `trigger` defaults to `contact_added`
  * Dashboard metadata (`description`, `labels`, `userCancellable`, and sequence BCC recipients) plus full From/Reply-To identity
  * `trigger: "inbound_webhook"` with integration metadata, in addition to list, tag, segment, event, inactivity, and frequency triggers
  * `trigger: "segment_entered"` plus `segmentId` for saved-segment entry automations
  * `goal` - AI generates email content
  * `durationDays` - total duration used to space AI-generated emails when using `goal`
  * `emailStyle` - `visual` (designed) or `plain` (text-first) for AI-generated emails; defaults to the company's saved preference
  * `steps` with `blocks` - Sequenzy JSON block format
  * `steps` with `html` - Any HTML (React Email, MJML, provider exports, etc.) preserved as one raw HTML block
  * `steps` with `attachments` - URL-backed file attachments (`[{ filename, path }]`) fetched at send time. For event-triggered sequences, `path` can use an event value such as `{{event.file_url}}`, and `filename` can also contain merge tags (max 10 per email, 7MB total)
  * `enrollmentMode: "matching_field"` for event-triggered product-, variant-, order-, or subscription-specific sequences that should block duplicate active runs only for the same resolved field
  * A scalar `enrollmentFieldPath`, such as `order.id` or `product.providerVariantId`, when you want `matching_field` to use a custom event property. Array traversal with `[]` is supported by `propertyFilters`, not enrollment keys
  * `propertyFilters` for `event_received` triggers - only start the sequence when the event properties match, e.g. scope a purchase sequence to one product with `{ "path": "lineItems[].providerProductId", "operator": "equals", "value": "prod_123" }` (or `productIds` for Stripe `saas.purchase` events)
  * For `event_received` sequences, step content can use `{{event.amount}}`, `{{event.order.id}}`, or other `{{event.*}}` merge tags from the event payload that enrolled the subscriber
  * Custom-event responses include `eventTrackingCode` plus `eventTracking`, which gives the endpoint, normalized trigger filters, a generated payload example, `examplePayloadMatchesFilters`, the direct docs URL, and arguments for `get_integration_guide`. When the example does not satisfy every filter automatically, follow `examplePayloadNote` and adapt it before sending
* `update_sequence` - Modify a sequence, target a specific step with the `emailId` or `nodeId` returned by `get_sequence`, atomically replace its typed trigger, or update `enrollmentMode` / `enrollmentFieldPath`. Set `bccEmails` to blind-copy team inboxes on every email the sequence sends (use `clearBccEmails` to remove them). Email steps also accept `attachments` (`[{ filename, path }]`) - URL-backed files fetched and attached at send time; `path` may be an `{{event.*}}` URL template resolved separately for each enrollment, and `[]` removes them. Tag, list, wait, condition, and webhook steps added through `insertSteps` or a `branch` path carry their node fields in `config`: `action_add_tag` / `action_remove_tag` take `tagName` (or `tagId` when you have the real tag ID), `action_add_to_list` / `action_remove_from_list` take `listId`, `logic_wait_for_event` takes `eventName` with `timeoutDays` / `timeoutAction`, `logic_condition` takes `conditionType` plus its resource field, and `action_webhook` takes `url` with optional `method` and `headers`; SMS, discount, and delay steps keep using their step-level fields
* `update_sequence_node` - Patch one existing node in place using its `nodeId`. It supports every stored node type: delays, email/SMS content, actions, conditions, branch configuration without topology changes, webhooks, and triggers. Pass the node's latest `updatedAt` as `expectedUpdatedAt` to prevent stale writes
* `update_sequence_nodes` - Apply multiple type-aware node patches atomically. Use this for bulk edits such as changing every 5-minute delay to 7 days or setting several email nodes to `{ "emailPreset": "minimal" }`; either every patch commits or none do
* `edit_sequence_graph` - Atomically move, reconnect, delete, or deep-copy existing sequence nodes using the latest `graphRevision`; deleting a step immediately moves parked recipients to its unique surviving successor, or completes them when no successor remains, and returns `migratedRecipientCount` / `completedRecipientCount`; active sequences require explicit structural-change confirmation
* `insert_sequence_step` - Insert a typed email, SMS, delay, discount, subscriber-update, tag/list, outbound webhook, condition, `logic_wait_for_event`, or `logic_branch` step. Webhooks accept URL, GET/POST method, and string-valued headers. Wait nodes accept `eventName`, `timeoutDays`, and `timeoutAction`. Branch paths accept typed conditions plus `targetNodeId`/`elseTargetNodeId`, new path steps, or both, so one atomic call can route a reply path to completion and Else to an existing follow-up
* `enable_sequence` / `disable_sequence` - Control status
* `duplicate_sequence` - Create an independent draft copy, including graph, emails, and sequence A/B tests
* `archive_sequence` / `unarchive_sequence` - Move a sequence into the dashboard archive or restore it as a draft
* `list_sequence_goals` / `create_sequence_goal` / `update_sequence_goal` / `delete_sequence_goal` - Manage the persisted event or subscriber-attribute conversion goals shown by the dashboard
* `get_sequence_inbound_webhook` / `configure_sequence_inbound_webhook` / `rotate_sequence_inbound_webhook_secret` - Read and configure the secret endpoint, field mapping, sample payload, and setup state attached to an inbound-webhook sequence trigger
* `enroll_subscribers_in_sequence` - Manually enroll up to 500 subscribers by email, subscriber ID, or both. Only active subscribers are enrolled: unknown emails are returned in `notFound`, and inactive, unavailable, or already enrolled subscribers are counted in `skipped`. Pass `targetNodeId` (a non-trigger `nodeId` from `get_sequence`) to start at a specific step instead of the first step after the trigger. The sequence must be accepting entrants.
* `cancel_sequence_enrollments` - Stop active or waiting enrollments in a required sequence. Target `cancelAll: true` to drain every enrollment (the right move when segment-triggered contacts share no entry field value, and the only way to stop contacts already mid-flight - pausing enrollment just blocks new entrants), `subscriberIds` for a batch of up to 500, `subscriberId` for one contact, or entry-event `fieldValues`. Field-value cancellation can use `fieldPath` or the sequence's configured `enrollmentFieldPath`. Every bulk target defaults to `dryRun: true`; pass `dryRun: false` to apply, then repeat the call while the response reports `remainingCount` above zero.

For node updates, call `get_sequence` immediately before writing. Start with the
returned node `config` and send only changed fields. Delay nodes use a readable
patch such as `{ "delay": { "days": 7 } }`, `delayMs`, or `waitUntil`. Node
updates for `action_email` can set `{ "emailPreset": "minimal" }` to change
only that linked email's **Style > Format** without changing the company theme.
This applies the same transformation as the dashboard to native Sequenzy
blocks, including emails that contain supported custom HTML blocks. An email
stored entirely as one standalone raw HTML block does not support `emailPreset`,
and `emailPreset` cannot be combined with `html` or `htmlContent`.
Existing node patches and arbitrary graph topology are intentionally separate:
use `insert_sequence_step` for a new typed branch or wait node, and use
`edit_sequence_graph` to add/remove branch paths, reconnect edges, reorder
nodes, or convert the flow structure. Updating an active sequence requires
`confirmLiveChange: true` after
the user confirms the impact. Recipients already waiting keep their existing
scheduled timestamp; the new delay applies to recipients that reach that node
after the update.

For the common “suppress the second email after a reply” flow, call
`get_sequence`, take the first email, follow-up, and completion node IDs, then
insert the branch in one request:

```json theme={null}
{
  "sequenceId": "seq_123",
  "type": "logic_branch",
  "afterNodeId": "node_email_1",
  "branches": [
    {
      "id": "replied",
      "conditionType": "event_received",
      "eventName": "email.replied",
      "activityScope": "this_sequence",
      "targetNodeId": "node_sequence_complete"
    }
  ],
  "elseTargetNodeId": "node_email_2"
}
```

### Transactional

* `list_transactional_emails` - Search and filter transactional templates by name, slug, subject/title, or active state; sort by delivery metrics; and return each template's dashboard URL
* `get_transactional_email` - Read a transactional email by ID or slug, including body blocks
* `create_transactional_email` - Create a transactional template from a prompt, raw HTML, or Sequenzy blocks. Prompt-generated templates include company branding with no unsubscribe link
* `update_transactional_email` - Update transactional email metadata or body content
* `send_email` - Send a single email using direct `subject` and `html` content (mapped to the transactional API's `subject` and `body` fields), or pass a saved transactional email API slug through the compatibility-named `templateId` field. Its `variables` object supports nested arrays for repeat blocks, such as `{ "event": { "items": [...] } }`. When the recipient matches a subscriber, saved first and last names fill omitted name variables; explicit `variables` values take precedence. The result echoes the accepted `emailType`; company Reply-To defaults are inherited, and `{{viewInBrowserUrl}}` becomes a hosted copy link.
* `get_transactional_stats` - Inspect top clicked links, complaints, replies, bounce classifications, and separate human/machine engagement for one saved template
* `list_email_sends` - Search recent delivery history and return a dashboard URL on every delivery; use a returned ID with `get_email_send` for the full timeline

### Segments

* `list_segments` - List saved segments
* `create_segment` - Create a segment from explicit filters, including nested AND/OR groups, event filters, segment filters, Stripe product purchase filters, and optional `filterJoinOperator`
* `update_segment` - Update a segment's name and/or replace its filter rules using the same `filters` plus `filterJoinOperator` or nested `root` shapes as `create_segment`
* `delete_segment` - Permanently delete a segment; subscribers are not affected
* `get_segment_count` - Preview how many active subscribers match a segment

For Stripe product segments, use the product-specific Stripe fields. The supported shapes are:

* `{"id":"filter-1","field":"stripeProduct","operator":"is","value":"prod_pro"}` for "bought product"
* `{"id":"filter-1","field":"stripeProduct","operator":"is_not","value":"prod_pro"}` for "didn't buy product"
* `{"id":"filter-1","field":"stripeProduct","operator":"at_least","value":"prod_pro:3"}` for "at least 3 payments"
* `{"id":"filter-1","field":"stripeProduct","operator":"less_than_count","value":"prod_pro:3"}` for "fewer than 3 payments"
* `{"id":"filter-1","field":"stripeCurrentProduct","operator":"is","value":"prod_pro"}` for "currently has product"
* `{"id":"filter-1","field":"stripeTrialProduct","operator":"is","value":"prod_pro"}` for "currently trialing product"
* `{"id":"filter-1","field":"stripeTrialProduct","operator":"is","value":"prod_pro:is_canceled"}` for "trialing product is set to cancel"
* `{"id":"filter-1","field":"stripeTrialProduct","operator":"gte","value":"prod_pro:start_at:7 days ago"}` for "trial started in the last 7 days"
* `{"id":"filter-1","field":"stripeTrialProduct","operator":"is","value":"prod_pro:end_at:2026-05-26"}` for "trial ends on May 26, 2026"

For products purchased through commerce orders (Shopify, WooCommerce, or the Commerce API), use the `commerceProduct` field. The value is `provider:productId` - product IDs are provider-scoped, so the provider prefix (`shopify`, `woocommerce`, or `api`) tells the filter which catalog the ID belongs to. A bare product ID matches the ID on any provider:

* `{"id":"filter-1","field":"commerceProduct","operator":"is","value":"api:prod-starter-kit"}` for "bought product"
* `{"id":"filter-1","field":"commerceProduct","operator":"is_not","value":"api:prod-starter-kit"}` for "didn't buy product"
* `{"id":"filter-1","field":"commerceProduct","operator":"at_least","value":"shopify:42:2"}` for "placed at least 2 orders containing the product"
* `{"id":"filter-1","field":"commerceProduct","operator":"less_than_count","value":"shopify:42:2"}` for "fewer than 2 orders containing the product"

To segment on a whole collection instead of one product, use the `commerceCollection` field. The value is a collection ID or handle, optionally provider-prefixed, with the same optional order-count threshold:

* `{"id":"filter-1","field":"commerceCollection","operator":"is","value":"skincare"}` for "bought anything from the collection"
* `{"id":"filter-1","field":"commerceCollection","operator":"is_not","value":"skincare"}` for "never bought from the collection"
* `{"id":"filter-1","field":"commerceCollection","operator":"at_least","value":"shopify:skincare:2"}` for "placed at least 2 orders from the collection"

Collection membership is resolved from the synced catalog at evaluation time, so it reflects which products are in the collection now rather than at purchase time.

For engagement filters (`emailSent`, `emailOpened`, `emailClicked`, `emailBounced`, `emailComplained`), the `value` can be a rolling time window (`7d`, `30d`, `90d`, `180d`, `all`), a specific sent campaign via `campaign:<campaign_id>`, or - with the `at_least` / `less_than_count` operators - a count with a time window in `count:timeRange` format:

* `{"id":"filter-1","field":"emailClicked","operator":"at_least","value":"10:all"}` for "clicked 10 or more times ever"
* `{"id":"filter-1","field":"emailOpened","operator":"less_than_count","value":"2:90d"}` for "opened fewer than 2 times in the last 90 days"

Combine two campaign-specific filters to express rules like "bounced campaign A but not campaign B":

* `{"id":"filter-1","field":"emailBounced","operator":"is","value":"campaign:cmp_abc"}`
* `{"id":"filter-2","field":"emailBounced","operator":"is_not","value":"campaign:cmp_xyz"}`

Use `list_campaigns` to look up the campaign IDs.

By default, segment filters use `AND` logic. To match any filter instead of all of them, pass `filterJoinOperator: "or"` when calling `create_segment`.

For nested logic, pass a v2 `root` group instead of `filters`:

```json theme={null}
{
  "name": "Active non-buyers",
  "root": {
    "kind": "group",
    "id": "root",
    "joinOperator": "and",
    "children": [
      {
        "kind": "filter",
        "id": "filter-1",
        "field": "attribute",
        "operator": "lte",
        "value": "last_login_days_ago:90"
      },
      {
        "kind": "group",
        "id": "group-1",
        "joinOperator": "or",
        "children": [
          {
            "kind": "filter",
            "id": "filter-2",
            "field": "event",
            "operator": "is_not",
            "value": "saas.purchase:30d"
          },
          {
            "kind": "filter",
            "id": "filter-3",
            "field": "segment",
            "operator": "is_not",
            "value": "seg_paying_customers"
          }
        ]
      }
    ]
  }
}
```

Event filter values use `eventName:range` for `is` / `is_not` and `eventName:count:range` for `at_least` / `less_than_count`. Segment filter values are saved segment IDs.

`list_segments` returns both:

* `subscriberCount` - all matched contacts, including unsubscribed or bounced contacts
* `activeSubscriberCount` - contacts eligible for campaigns

`get_segment_count` returns the active count, because campaigns and most send flows only target active subscribers.

### Audience Syncs

Push segments to Meta custom audiences for Facebook and Instagram retargeting. Requires the Meta Ads integration to be connected in the dashboard (Settings → Integrations).

* `list_audience_syncs` - List segment-to-audience syncs with schedule and last sync status
* `list_ad_accounts` - List the Meta ad accounts available for syncing
* `create_audience_sync` - Create a sync from an existing segment (`segmentId`) or a ready-made template (`predefinedSegmentId`, for example `zero-ltv`, `no-purchase-1y`, `recent-buyers`); the first upload runs immediately
* `update_audience_sync` - Change the frequency (`hourly`, `daily`, `weekly`) or pause/resume via `isActive`
* `delete_audience_sync` - Remove a sync; the Meta audience itself is kept
* `sync_audience_now` - Trigger an immediate upload outside the schedule

Audiences are add-only: subscribers who leave the segment stay in the Meta audience. Meta requires 100+ matched people before an audience can be used for ad delivery.

### Products & Digital Delivery

* `list_products` - List synced products (Stripe, Shopify, WooCommerce, api) including any attached delivery file
* `upsert_products` - Create or update products in the catalog (Commerce API, keyed by your `productId`, up to 100 per call)
* `delete_product` - Delete a product previously pushed via `upsert_products`
* `attach_product_file` - Attach a distributable file to a product, delivered after purchase. Pass `url` for a hosted file, or `filePath` to upload a local file (local MCP server only)
* `remove_product_file` - Remove the attached file from a product
* `sync_products` - Queue a sync of the Stripe product catalog

After a file is attached, purchases of the product enrich the `saas.purchase` event with `download.url` and `download.name`, so purchase sequences can deliver the file with `{{event.download.url}}`. See [Digital Product Delivery](/concepts/digital-products).

### Image Assets

* `upload_image_asset` - Upload a PNG, JPEG, GIF, or WebP image (up to 5MB) to
  the selected company's shared media library. The tool returns the hosted
  `asset.url` and a complete `imageBlock` that can be inserted into campaign,
  sequence, saved-template, or transactional-email blocks.

Use `filePath` when the local stdio MCP server runs on the same machine as the
image. For a remote MCP connector, use `imageBase64` plus `filename` when the
client can expose the attachment bytes. This keeps binary image data out of
`create_sequence` and `update_sequence` while still making the resulting URL
available to every block-based email surface.

For a responsive lifecycle screenshot, set `displayWidthPercent: 100`. Add
`cropHeight` with `objectFit: "cover"` for a fixed-height centered crop, or use
`"contain"` when the full screenshot must remain visible. `altText` is stored
with the media asset and returned as `imageBlock.alt`.

```json theme={null}
{
  "filePath": "/Users/me/Desktop/product-results.png",
  "altText": "Product results dashboard",
  "displayWidthPercent": 100,
  "cropHeight": 320,
  "objectFit": "cover",
  "align": "center"
}
```

Then copy the returned `imageBlock` into the exact email step's `blocks` array
with `update_sequence` (or the corresponding campaign/template tool). Image
upload alone never changes or sends an email.

### Tags

* `list_tags` - List all tags in the account
* `create_tag` - Create a tag definition. Names are normalized to lowercase with hyphens (`VIP Customer` becomes `vip-customer`); color defaults to `gray`
* `update_tag` - Update a tag's color; system tags cannot be changed
* `delete_tag` - Permanently delete a tag and remove it from all subscribers; system tags and tags used by sequences cannot be deleted

### Templates

* `list_templates` - List all email templates with localization status by locale
* `get_template` - Get a template's details, content, and localized variants
* `create_template` - Create a template from raw HTML or Sequenzy blocks
* `update_template` - Update template metadata, inbox preview text, raw HTML, Sequenzy blocks, or labels
* `set_template_localization` - Create or replace a caller-supplied localized variant
* `sync_template_localizations` - Queue AI translation for selected or all enabled non-primary locales

### A/B Tests

* `list_ab_tests` - List A/B tests and variants, optionally scoped by sequence
* `get_ab_test` - Get effective settings, variants, content, and localization status. Copy the returned `settings` object; sequence `testPercentage: 100` and `testDurationMinutes: 0` values are legacy sentinels
* `get_ab_test_stats` - Get aggregate and per-variant stats
* `create_ab_test` - Provide exactly one of `campaignId` or `automationNodeId`. Campaign tests use `testPercentage`, `testDurationMinutes`, and `winnerCriteria`; a sequence email node is converted to `action_ab_test` with `testType`, `winnerThreshold`, and `winnerCriteria`. An explicit sequence `winnerCriteria` overrides the test-type default. Pass `confirmLiveChange: true` when converting a node in an active sequence. Control variant A is created automatically
* `update_ab_test` - Update campaign or sequence settings. Campaign tests accept percentage/duration/criteria; sequence tests accept type/threshold/criteria and require `confirmLiveChange: true` for active or already-used tests
* `add_ab_test_variant` - Add a variant to a draft campaign or sequence A/B test; sequence variants receive independent email templates
* `update_ab_test_variant` - Update a draft variant's subject, preview text, HTML, or blocks
* `delete_ab_test_variant` - Permanently remove a variant from a draft campaign or sequence A/B test; variant A is the control and cannot be deleted
* `delete_ab_test` - Permanently delete a campaign A/B test and all of its variants; running tests cannot be deleted

Use `get_ab_test` to discover variant IDs before editing. `update_ab_test_variant` accepts either `html` or `blocks`, not both, and only draft tests can be changed. Campaign creation requires a draft or rejected campaign; sequence creation requires an `action_email` node. Variants can only be added or removed while the test is in draft status. When a sequence test's parent sequence is active, `add_ab_test_variant` and `delete_ab_test_variant` also require `confirmLiveChange: true` because they immediately change the live rotation.

When you need recipient-specific content, prefer Sequenzy blocks instead of raw HTML. Every block accepts a `conditions` array so it renders only when its rules match. To branch on a value passed in a transactional send's `variables` or an automation `event` payload, use `field: "variable"`:

```json theme={null}
{
  "name": "Launch",
  "subject": "Big update",
  "blocks": [
    {
      "id": "intro",
      "type": "text",
      "content": "<p>Hello there</p>",
      "variant": "paragraph"
    },
    {
      "id": "pro-offer",
      "type": "text",
      "content": "<p>Your Pro upgrade is ready.</p>",
      "variant": "paragraph",
      "conditions": [
        {
          "id": "c1",
          "field": "variable",
          "operator": "is",
          "value": "plan:pro"
        }
      ]
    }
  ]
}
```

For a `variable` condition, the text before the colon in `value` is a merge-tag path (without `{{ }}`), including nested paths like `order.total` or `event.plan`, and the text after it is the comparison value. `field` can also be `attribute` (same `name:value` form) or `email` / `firstName` / `lastName` (the value is the plain comparison string). Operators: `is`, `is_not`, `contains`, `not_contains`, `gt`, `gte`, `lt`, `lte`, `is_empty`, `is_not_empty`.

For if/else, use a `conditional-group` block with `ifBranch` and `elseBranch`:

```json theme={null}
{
  "type": "conditional-group",
  "conditions": [
    { "id": "c1", "field": "variable", "operator": "is", "value": "plan:pro" }
  ],
  "ifBranch": {
    "children": [
      {
        "type": "text",
        "content": "<p>Pro perks inside.</p>",
        "variant": "paragraph"
      }
    ]
  },
  "elseBranch": {
    "children": [
      {
        "type": "text",
        "content": "<p>Upgrade to Pro.</p>",
        "variant": "paragraph"
      }
    ]
  }
}
```

### Team

* `list_team_members` - List the company owner, members with their roles, and pending or expired invitations
* `invite_team_member` - Invite a teammate as `admin`, `viewer`, or `restricted`. Existing Sequenzy users are added immediately; everyone else receives an email invitation. Billing access (`canManageBilling`) can only be granted by the company owner and is not available for restricted members
* `cancel_team_invitation` - Cancel a pending invitation; accepted invitations cannot be cancelled

### Inbox

* `list_conversations` - List subscriber reply conversations with status, search, unread, and pagination filters
* `get_conversation` - Get a conversation with its full message history, subscriber details, and context
* `reply_to_conversation` - Send an outbound reply (requires `bodyText` or `bodyHtml`) or add an internal team-only note with `type: "note"`. Replying to a closed conversation reopens it
* `update_conversation_status` - Open or close a conversation
* `mark_conversation_read` - Mark all unread inbound messages in a conversation as read

### Webhooks

* `list_webhooks` - List outbound webhook endpoints and their subscribed event types
* `create_webhook` - Create a webhook endpoint. The response includes a `signingSecret` that is returned only once - store it immediately to verify webhook signatures. If `events` is omitted, a default set of event types is subscribed
* `update_webhook` - Update a webhook's name, URL, subscribed events, or enabled/disabled status; providing `events` replaces the existing list
* `delete_webhook` - Permanently delete a webhook endpoint and stop all deliveries to it
* `test_webhook` - Send a test event to verify the endpoint is reachable and signatures validate
* `list_webhook_deliveries` - List recent delivery attempts, including status and response codes
* `replay_webhook_delivery` - Re-send a previous delivery's event payload to the endpoint

Webhook event types cover `email.*`, `sms.*`, `subscriber.*`, and `sequence.*` events.

### Analytics

* `get_stats` - Overview stats (7d/30d/90d). Counts are a funnel over the sends made inside the period: `opened` and `clicked` are unique per email send (not total open events) and include engagement that arrives after the period ends, so they never exceed `sent`. Rates divide by `rateDenominator` (`delivered`, falling back to `sent`), returned with `rateDenominatorBasis`. Set `emailType` to `transactional` for Send API and transactional SMTP open/click rates, including direct and saved-template sends
* `get_transactional_stats` - Aggregate metrics for one saved transactional email by ID or slug, all-time or within a requested period/range
* `list_email_sends` - Search and filter the recent dashboard delivery history by subject/title, recipient, status, type, bounce type, or source ID
* `get_email_send` - Inspect one delivery's status, timestamps, failure details, stored HTML, and event timeline
* `get_campaign_stats` - Campaign performance, plus a top-level `clickedLinks` array with the per-link click breakdown, and a top-level `polls` array with answer distributions and NPS score/breakdown when the campaign collected survey responses
* `get_sequence_stats` - Sequence performance plus live active/waiting enrollment-run counts grouped by current node; historical date filters do not limit the live counts
* `get_subscriber_activity` - Individual activity

### AI Generation

* `generate_email` - Create email blocks from a prompt. Company logo/footer branding is included by default; use `applyBranding: false` for raw content blocks, or `emailType: "transactional"` for a footer without an unsubscribe link
* `generate_sequence` - Deprecated compatibility alias for goal-based `create_sequence`; it persists the same disabled draft
* `generate_subject_lines` - Generate A/B subject variants
* `generate_sms` - Generate SMS message variants with encoding and segment counts

Generated email content is returned as draft content for review.
`create_sequence` persists a disabled draft automation that appears in
`list_sequences`; the deprecated `generate_sequence` alias does the same.
Email and sequence generation include the company's configured email branding by
default. Sequence generation supports up to 10 emails.

### SMS

* `get_sms_settings` - SMS add-on status: enabled, plan eligibility, credit balance, brand prefix, numbers, and a `readyToSend` flag that accepts either a paid plan or SMS credits plus an active number
* `send_test_sms` - Send a real test text (charges credits, max 5/hour, bypasses quiet hours)

Sequences support native SMS steps: pass `type: "sms"` with a plain-text `text` field in `create_sequence` steps or `insert_sequence_step`, and edit existing SMS steps with `update_sequence.smsSteps` (targeted by `action_sms` nodeId). Check `get_sms_settings` first and warn the user when SMS is not ready - steps added early are stored but skip at runtime until the add-on is enabled and a number is verified.

### Feedback

* `submit_feedback` - Send product feedback about Sequenzy to the Sequenzy team

Agents should call `submit_feedback` whenever the user wanted to accomplish something the tools do not expose - a missing tool or argument, an unsupported workflow, or a broken behavior. Categories: `missing_capability`, `bug`, `docs`, `ux`, `praise`, `other`. Every submission goes straight to the team and shapes what gets built next.

## Example Conversations

### Set up email for a new SaaS

```
You: "Set up email sequences for my SaaS that has a 14-day trial"

Claude: I'll help set that up. First, let me check your current sequences...
[Uses list_sequences]

You don't have any sequences yet. I'll create three for you:
1. Welcome sequence (triggered on signup)
2. Trial nurture (days 1, 3, 7, 10, 13)
3. Trial ending reminder (day 12)

[Uses create_sequence for each]

Done! I've created 3 sequences with 8 emails total.
```

### Add a subscriber

```
You: "Add john@example.com as a VIP subscriber"

Claude: [Uses add_subscriber]
Added john@example.com with the VIP tag.
```

### Publish a landing page

```
You: "Create a landing page for our product waitlist and publish it"

Claude: [Uses create_landing_page with template "waitlist", then publish_landing_page]
Done. I created and published the waitlist page. The public URL is ready, and I can connect pages.example.com if you want a branded domain.
```

### Create a Stripe product segment

```
You: "Create a segment for subscribers who bought Stripe product prod_pro at least 3 times"

Claude: [Uses create_segment with filters:
{"id":"filter-1","field":"stripeProduct","operator":"at_least","value":"prod_pro:3"}]
Done. I created the segment and saved it for future campaigns.
```

### Create a match-any segment

```
You: "Create a segment for VIP contacts or anyone on Gmail"

Claude: [Uses create_segment with:
{"filterJoinOperator":"or","filters":[{"id":"filter-1","field":"tag","operator":"contains","value":"vip"},{"id":"filter-2","field":"email","operator":"contains","value":"@gmail.com"}]}]
Done. I created a segment that matches either condition.
```

### Check performance

```
You: "How did my last campaign perform?"

Claude: [Uses list_campaigns then get_campaign_stats]
Your "Product Launch" campaign sent yesterday had:
- 2,451 sent
- 67.2% open rate
- 12.3% click rate
That's above your average!
```

### Create a sequence with specific content

```
You: "Create a 3-email welcome sequence with specific content.
Email 1 (immediate): Subject 'Welcome!' - 'Thanks for joining us.'
Email 2 (1 day later): Subject 'Getting Started' - 'Here's how to begin.'
Email 3 (3 days later): Subject 'Pro Tips' - 'Unlock advanced features.'"

Claude: [Uses create_sequence with steps]
Done! I've created the welcome sequence with 3 emails:
1. "Welcome!" - sends immediately
2. "Getting Started" - sends after 1 day
3. "Pro Tips" - sends after 3 days

The sequence is in draft mode. Would you like me to enable it?
```

### Migrate a sequence from another platform

```
You: "I have these React Email templates for my onboarding sequence.
Can you create a sequence that sends them?"

[Pastes HTML content]

Claude: [Uses create_sequence with steps using html field]
Done! I've created your onboarding sequence with the HTML content
you provided. The HTML was automatically converted to Sequenzy's
block format so you can edit it in the visual editor later.

The sequence is ready in draft mode with 3 emails.
```

## Event-Personalized Sequences

When your AI assistant creates a sequence with `trigger: "event_received"`, it can place `{{event.*}}` merge tags directly into step subjects or body content.

Those values come from the `properties` payload you send to `POST /api/v1/subscribers/events`.

Example:

* Trigger payload: `{"event":"weather.wind_alert","properties":{"city":"Tel Aviv","alert":{"maxSpeed":75}}}`
* Sequence subject: `Wind alert for {{event.city}}`
* Sequence body: `Winds may reach {{event.alert.maxSpeed}} km/h.`

Sequenzy stores that event snapshot on the sequence run, so delayed follow-up emails keep using the same original event values.

## Resources

MCP also provides read-only resources that AI can access:

| Resource                        | Description                                                 |
| ------------------------------- | ----------------------------------------------------------- |
| `sequenzy://dashboard`          | Live 7-day overview stats                                   |
| `sequenzy://company`            | Currently selected company, including localization settings |
| `sequenzy://campaigns/recent`   | Last 10 campaigns                                           |
| `sequenzy://subscribers/recent` | Recently added subscribers                                  |
| `sequenzy://sequences`          | All sequences with status                                   |
| `sequenzy://templates`          | Available email templates with localization status          |
| `sequenzy://segments`           | Defined segments                                            |
| `sequenzy://tags`               | All tags with counts                                        |

## Security

* Personal API keys are tied to your user account
* Keys can only access companies you have access to
* You can revoke keys anytime in Settings → API Keys
* Keys are never shared between users

## Troubleshooting

Sequenzy MCP failures now return a short explanation, a concrete recovery step, and a direct `docs.sequenzy.com` link so AI clients can self-correct more reliably.

### Duplicate resources

If a tool call would create a duplicate segment name or sending domain, the error includes a stable `code`, an agent-friendly `description`, a concrete `resolution`, and a `docsUrl`. For segments, call `list_segments` and reuse the existing segment ID or choose a different name. For websites, call `list_websites`; if the domain is not listed for the selected company, it belongs to another company or account and must be removed, reassigned, or replaced with a different sending domain.

### "SEQUENZY\_API\_KEY environment variable is required"

Make sure your API key is set in the MCP config. Run `npx @sequenzy/setup` to configure automatically.

### "Invalid API key"

Your key may have been revoked. Create a new one in Settings → API Keys.

### Changes not appearing

Restart your AI client after modifying the MCP config.
