From 9639bc7c21e5653b9dd39f4254f6c24b6e2bc76c Mon Sep 17 00:00:00 2001 From: voj-tech-j Date: Thu, 17 Sep 2026 15:48:31 +0200 Subject: [PATCH 1/2] docs: describe the API behaviour changed in lettr#489 Email history pagination takes next_cursor back unchanged, pulls an old `from` forward to the retention limit and requires `to` after `from`. Unschedule also clears a delivery time staged on a draft. Lists that segments are scoped to cannot be deleted (409 list_in_use), segments reject unsupported conditions, a contact's list_id must exist, and webhook names are capped at 24 characters with updates needing a field. Co-Authored-By: Claude Opus 5 (1M context) --- .../audience/bulk-delete-audience-lists.mdx | 2 +- api-reference/audience/create-a-contact.mdx | 2 +- api-reference/audience/delete-an-audience-list.mdx | 2 +- api-reference/campaigns/unschedule-a-campaign.mdx | 2 +- api-reference/introduction.mdx | 3 +++ learn/audience/double-opt-in.mdx | 2 +- learn/audience/lists.mdx | 4 +++- learn/audience/segments.mdx | 6 ++++++ learn/sending/email-history.mdx | 14 +++++++++----- learn/webhooks/authorization.mdx | 2 +- learn/webhooks/introduction.mdx | 4 ++++ quickstart/go/webhooks.mdx | 2 +- quickstart/laravel/webhooks.mdx | 2 +- quickstart/nodejs/webhooks.mdx | 2 +- quickstart/php/webhooks.mdx | 2 +- quickstart/python/webhooks.mdx | 2 +- 16 files changed, 36 insertions(+), 17 deletions(-) diff --git a/api-reference/audience/bulk-delete-audience-lists.mdx b/api-reference/audience/bulk-delete-audience-lists.mdx index fc8313c..fb7f23e 100644 --- a/api-reference/audience/bulk-delete-audience-lists.mdx +++ b/api-reference/audience/bulk-delete-audience-lists.mdx @@ -3,4 +3,4 @@ title: Bulk delete audience lists openapi: DELETE /audience/lists/bulk --- -Delete up to 50 lists in a single request. All `list_ids` must belong to the authenticated team. Requires the `audience:write` scope and is blocked for sandbox API keys. +Delete up to 50 lists in a single request. All `list_ids` must belong to the authenticated team. If any list has segments scoped to it, nothing is deleted and the response is `409` `list_in_use`. Requires the `audience:write` scope and is blocked for sandbox API keys. diff --git a/api-reference/audience/create-a-contact.mdx b/api-reference/audience/create-a-contact.mdx index 686562e..6ff19db 100644 --- a/api-reference/audience/create-a-contact.mdx +++ b/api-reference/audience/create-a-contact.mdx @@ -3,4 +3,4 @@ title: Create a contact openapi: POST /audience/contacts --- -Create a single contact. If `double_opt_in` is provided the contact is created in `unverified` status and receives a confirmation email — they become `subscribed` only after clicking the confirmation link. Requires the `audience:write` scope and is blocked for sandbox API keys. +Create a single contact. A `list_id` must be a list of your team, otherwise the response is `422` and no contact is created. If `double_opt_in` is provided the contact is created in `unverified` status and receives a confirmation email — they become `subscribed` only after clicking the confirmation link. Requires the `audience:write` scope and is blocked for sandbox API keys. diff --git a/api-reference/audience/delete-an-audience-list.mdx b/api-reference/audience/delete-an-audience-list.mdx index bf12cd9..e25ae4e 100644 --- a/api-reference/audience/delete-an-audience-list.mdx +++ b/api-reference/audience/delete-an-audience-list.mdx @@ -3,4 +3,4 @@ title: Delete an audience list openapi: DELETE /audience/lists/{listId} --- -Permanently delete an audience list. Requires the `audience:write` scope and is blocked for sandbox API keys. +Permanently delete an audience list. A list that segments are scoped to cannot be deleted: the response is `409` `list_in_use`, naming the segments. Requires the `audience:write` scope and is blocked for sandbox API keys. diff --git a/api-reference/campaigns/unschedule-a-campaign.mdx b/api-reference/campaigns/unschedule-a-campaign.mdx index 58f52dc..074997a 100644 --- a/api-reference/campaigns/unschedule-a-campaign.mdx +++ b/api-reference/campaigns/unschedule-a-campaign.mdx @@ -3,4 +3,4 @@ title: Unschedule a campaign openapi: POST /campaigns/{campaignId}/unschedule --- -Cancels a scheduled send, returning the campaign to `draft`. The campaign must currently be `scheduled`. Requires the `campaigns:write` scope. Not available to sandbox keys. +Cancels a scheduled send, returning the campaign to `draft`. Also clears a delivery time staged on a `draft` in the editor, so the draft can then be sent immediately. A draft without a delivery time returns `422` `campaign_not_scheduled`. Requires the `campaigns:write` scope. Not available to sandbox keys. diff --git a/api-reference/introduction.mdx b/api-reference/introduction.mdx index aa4c32a..50aa1d2 100644 --- a/api-reference/introduction.mdx +++ b/api-reference/introduction.mdx @@ -75,6 +75,7 @@ curl -X POST https://app.lettr.com/api/emails \ | `200` | Success - Email queued for delivery | | `400` | Bad Request - Domain configuration error | | `401` | Unauthorized - Invalid or missing API key | +| `409` | Conflict - The resource already exists or is still in use | | `422` | Unprocessable Entity - Validation failed | | `429` | Too Many Requests - Rate limit or sending quota exceeded | | `502` | Bad Gateway - Upstream transmission failed | @@ -91,6 +92,8 @@ curl -X POST https://app.lettr.com/api/emails \ | `quota_exceeded` | 429 | Monthly sending quota exceeded. Upgrade your plan to continue sending. | | `daily_quota_exceeded` | 429 | Daily sending quota exceeded (free tier). Try again tomorrow. | | `transmission_failed` | 502 | Email transmission to the upstream provider failed. | +| `resource_already_exists` | 409 | The resource already exists, for example a sending domain your team has already added. | +| `list_in_use` | 409 | The audience list cannot be deleted because segments are scoped to it. The message names the segments. | ## Send Email Reference diff --git a/learn/audience/double-opt-in.mdx b/learn/audience/double-opt-in.mdx index 8e51b5c..77017df 100644 --- a/learn/audience/double-opt-in.mdx +++ b/learn/audience/double-opt-in.mdx @@ -94,7 +94,7 @@ curl -X POST https://app.lettr.com/api/audience/contacts \ }' ``` -The API responds immediately. The contact is created with status `unverified`, and the confirmation email is queued for delivery. If you also pass `properties` or `list_id` in the call, they're attached to the contact right away — they don't wait for confirmation. +The API responds immediately. The contact is created with status `unverified`, and the confirmation email is queued for delivery. If you also pass `properties` or `list_id` in the call, they're attached to the contact right away — they don't wait for confirmation. A `list_id` must belong to your team; an unknown one returns `422` and no contact is created. ### Required `double_opt_in` Fields diff --git a/learn/audience/lists.mdx b/learn/audience/lists.mdx index c821b11..47ddb63 100644 --- a/learn/audience/lists.mdx +++ b/learn/audience/lists.mdx @@ -58,6 +58,8 @@ You can rename a list at any time from the **Lists** page. Renaming does not aff Deleting a list removes the list itself and the membership records, but does **not** delete the contacts. The contacts remain in your audience and in any other lists or segments they belong to. +A list that a segment is scoped to cannot be deleted. Deleting it would remove the segment's list restriction and widen the segment to your whole audience. Remove the list from those segments, or delete the segments, first. The API returns `409` with `error_code` `list_in_use` and names the segments; a bulk delete deletes nothing if any of its lists is in use. + Segments reference lists by **name**, not by ID. If you rename a list that a segment uses in its conditions, that segment will silently stop matching anyone from the renamed list until you update the segment. Always check your segments after renaming a list. @@ -72,7 +74,7 @@ A contact who belongs to multiple selected lists is only sent the email once. Se When you create a segment, you can optionally scope it to a single list. The segment then only matches contacts who are members of that list **and** match the conditions. This is useful for further filtering a list — for example, "everyone in the Q4 onboarding list whose `last_login` is older than 30 days" — without affecting other contacts. -See [Segments](/learn/audience/segments#list-scoped-segments) for details. +A list with segments scoped to it cannot be deleted until those segments no longer use it. See [Segments](/learn/audience/segments#list-scoped-segments) for details. ## Lists vs. Topics vs. Segments diff --git a/learn/audience/segments.mdx b/learn/audience/segments.mdx index ffceef8..3e872bf 100644 --- a/learn/audience/segments.mdx +++ b/learn/audience/segments.mdx @@ -94,6 +94,10 @@ Each condition uses an operator that depends on the field type. Lettr supports t For `topics` and `lists` fields, `equals` means "is a member" and `not_equals` means "is not a member". + + Only the fields above are supported, and `topics` and `lists` only take `equals` and `not_equals`. A condition on any other field (for example `city` instead of `properties.city`, or `Email` with a capital E), or `topics`/`lists` with another operator, is rejected when you save the segment; the API returns `422`. A segment saved before this check with such a condition treats it as matching no contacts, so review segments that suddenly match fewer contacts than expected. + + `is_empty` and `is_not_empty` need no value. They are the quickest way to find gaps in your data. For example, `language is empty` lists every contact who would receive the primary language of a [multilingual campaign](/learn/multilingual-campaigns/contact-language#finding-contacts-without-a-language) because Lettr has nothing to match. ## AI Segment Builder @@ -120,6 +124,8 @@ A segment can optionally be scoped to a single [list](/learn/audience/lists). Wh List-scoped segments are useful when you want to slice a specific list further without affecting the rest of your audience. For example, a list called "Onboarding Cohort October 2026" can have its own segments for "completed the welcome tour" or "didn't open the first email" — without those segments running over your entire audience. +While a segment is scoped to a list, that list cannot be deleted. Remove the list from the segment first, or delete the segment. + ## Renaming Lists and Topics This is the most common gotcha with segments: diff --git a/learn/sending/email-history.mdx b/learn/sending/email-history.mdx index 0fd1bbf..32d7b02 100644 --- a/learn/sending/email-history.mdx +++ b/learn/sending/email-history.mdx @@ -19,10 +19,14 @@ curl -X GET "https://app.lettr.com/api/emails" \ | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `per_page` | integer | No | Number of results per page (1-100, default: 25) | -| `cursor` | string | No | Pagination cursor for next page | +| `cursor` | string | No | The `next_cursor` from a previous response, passed back unchanged. Any other value returns `422`. | | `recipients` | string | No | Filter by recipient email address | -| `from` | string | No | Filter emails sent after this date (ISO 8601) | -| `to` | string | No | Filter emails sent before this date (ISO 8601) | +| `from` | string | No | Filter emails sent at or after this date (ISO 8601). Defaults to 10 days ago. | +| `to` | string | No | Filter emails sent before this date (ISO 8601, exclusive). Must be after `from`. | + + + Email history is kept for about 10 days. A `from` older than that is moved forward to the oldest available day, and the response's `from` shows the window that was actually searched. A window that ends before the retention limit returns an empty list. + ### Response @@ -177,7 +181,7 @@ curl -X GET "https://app.lettr.com/api/emails?recipients=customer@example.com" \ ## Pagination -Use cursor-based pagination to retrieve large result sets: +Use cursor-based pagination to retrieve large result sets. `next_cursor` is an opaque token: pass it back unchanged as `cursor`. It carries the filters and time window of the first request, so later pages return the same shape and window. When `next_cursor` is `null`, there are no more pages. ```javascript async function getAllEmails() { @@ -297,7 +301,7 @@ async function getDailySendReport() { |--------|-------|-------------| | 401 | Unauthorized | Invalid or missing API key | | 404 | Not Found | Email with specified request ID not found | -| 422 | Validation Error | Invalid query parameters | +| 422 | Validation Error | Invalid query parameters, a `cursor` that is not a `next_cursor` from a previous response, or `to` not after `from`. Fix the request instead of retrying. | | 500 | Server Error | Internal error, retry the request | ## Related Topics diff --git a/learn/webhooks/authorization.mdx b/learn/webhooks/authorization.mdx index 794af88..c7a56e9 100644 --- a/learn/webhooks/authorization.mdx +++ b/learn/webhooks/authorization.mdx @@ -216,7 +216,7 @@ The response includes the auth type and whether credentials are configured: ``` - The API response shows `auth_type` and `has_auth_credentials` but never exposes the actual credentials. To update authentication settings, use the Lettr dashboard. + The API response shows `auth_type` and `has_auth_credentials` but never exposes the actual credentials. To change authentication settings, use the Lettr dashboard or `PUT /api/webhooks/{webhookId}`. Credentials are only saved together with `auth_type`: `basic` needs `auth_username` and `auth_password`, and `oauth2` needs `oauth_client_id`, `oauth_client_secret` and `oauth_token_url`. Sending credentials without `auth_type` returns `422`. ## Related Topics diff --git a/learn/webhooks/introduction.mdx b/learn/webhooks/introduction.mdx index 73e0df3..5e7cefe 100644 --- a/learn/webhooks/introduction.mdx +++ b/learn/webhooks/introduction.mdx @@ -276,6 +276,10 @@ curl -X DELETE "https://app.lettr.com/api/webhooks/{webhookId}" \ -H "Authorization: Bearer lttr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` + + A webhook name can be at most 24 characters. An update must change at least one of `name`, `url`, `auth_type`, `events` or `active`; an empty update returns `422`. To change credentials, send them together with `auth_type` (see [Authorization](/learn/webhooks/authorization)). + + The response for retrieving a webhook includes the configuration and its current status: ```json diff --git a/quickstart/go/webhooks.mdx b/quickstart/go/webhooks.mdx index 90d3e1a..76967b0 100644 --- a/quickstart/go/webhooks.mdx +++ b/quickstart/go/webhooks.mdx @@ -67,7 +67,7 @@ webhook, err := client.Webhooks.Get(ctx, "webhook-id") ## Update a Webhook -Set only the fields you want to change. Optional booleans like `Active` are pointers: +Set only the fields you want to change (at least one). Optional booleans like `Active` are pointers: ```go active := false diff --git a/quickstart/laravel/webhooks.mdx b/quickstart/laravel/webhooks.mdx index 07bb8df..048477d 100644 --- a/quickstart/laravel/webhooks.mdx +++ b/quickstart/laravel/webhooks.mdx @@ -100,7 +100,7 @@ if ($webhook->listensTo(WebhookEventType::MessageBounce)) { ## Update a Webhook -All fields are optional — only the ones you set are sent: +All fields are optional — only the ones you set are sent. Set at least one; an empty update is rejected: ```php use Lettr\Dto\Webhook\UpdateWebhookData; diff --git a/quickstart/nodejs/webhooks.mdx b/quickstart/nodejs/webhooks.mdx index fca8eed..4fa34d4 100644 --- a/quickstart/nodejs/webhooks.mdx +++ b/quickstart/nodejs/webhooks.mdx @@ -77,7 +77,7 @@ if (!error) { ## Update a Webhook -All fields are optional — only the ones you set are sent: +All fields are optional — only the ones you set are sent. Set at least one; an empty update is rejected: ```typescript await client.webhooks.update("webhook-id", { diff --git a/quickstart/php/webhooks.mdx b/quickstart/php/webhooks.mdx index 46bb1c3..bd6c8aa 100644 --- a/quickstart/php/webhooks.mdx +++ b/quickstart/php/webhooks.mdx @@ -98,7 +98,7 @@ if ($webhook->listensTo(WebhookEventType::MessageBounce)) { ## Update a Webhook -All fields are optional — only the ones you set are sent: +All fields are optional — only the ones you set are sent. Set at least one; an empty update is rejected: ```php use Lettr\Dto\Webhook\UpdateWebhookData; diff --git a/quickstart/python/webhooks.mdx b/quickstart/python/webhooks.mdx index a8c3edc..1088889 100644 --- a/quickstart/python/webhooks.mdx +++ b/quickstart/python/webhooks.mdx @@ -74,7 +74,7 @@ print(webhook.last_successful_at, webhook.last_failure_at) ## Update a Webhook -Pass only the fields you want to change: +Pass only the fields you want to change (at least one): ```python client.webhooks.update( From 88b0d9369b1d36591716c298c3007f6f43ad0f2f Mon Sep 17 00:00:00 2001 From: voj-tech-j Date: Thu, 17 Sep 2026 15:56:03 +0200 Subject: [PATCH 2/2] docs(mcp): document the remote server's full tool set The remote MCP server now registers 77 tools, reaching parity with the lettr-mcp package, but the pages still marked domain management, template delete, merge tags and webhook details as local only and listed no campaign, audience, scheduling or email history tools. The availability table now has a row per tool on either server (77 remote, 69 local), every tool has a reference section, stale parameter tables are corrected (idempotency_key, optional subject and folder_id, template purpose, slug and domain identifiers), and the local setup page lists the package's 69 tools instead of 14. Co-Authored-By: Claude Opus 5 (1M context) --- learn/domains/sending-domains.mdx | 2 +- learn/mcp/introduction.mdx | 31 +- learn/mcp/local-setup.mdx | 104 ++- learn/mcp/setup.mdx | 76 +- learn/mcp/tools-reference.mdx | 1166 +++++++++++++++++++++++++---- 5 files changed, 1217 insertions(+), 162 deletions(-) diff --git a/learn/domains/sending-domains.mdx b/learn/domains/sending-domains.mdx index 1137ecf..9f99a44 100644 --- a/learn/domains/sending-domains.mdx +++ b/learn/domains/sending-domains.mdx @@ -64,7 +64,7 @@ Response with DNS records to configure: ``` - Working in an AI-powered editor like Claude Code or Cursor? The [local Lettr MCP server](/learn/mcp/local-setup) can handle this whole flow for you — its `create-domain` and `verify-domain` tools let your assistant add the domain, hand you the exact DNS records, and re-run verification until everything passes. + Working with an AI assistant like Claude, ChatGPT, Claude Code, or Cursor? Both Lettr MCP servers can handle this whole flow for you — the [remote server](/learn/mcp/setup)'s `create_domain` and `verify_domain` tools and the [local server](/learn/mcp/local-setup)'s `create-domain` and `verify-domain` tools let your assistant add the domain, hand you the exact DNS records, and re-run verification until everything passes. On the remote server, `diagnose_sending_domain_dns` also explains what is still wrong with your records. ## DNS Records Configuration diff --git a/learn/mcp/introduction.mdx b/learn/mcp/introduction.mdx index 32e9a6b..3b5e495 100644 --- a/learn/mcp/introduction.mdx +++ b/learn/mcp/introduction.mdx @@ -3,7 +3,7 @@ title: "MCP for AI Assistants" description: "Connect AI assistants to Lettr with the Model Context Protocol to send email, manage templates, and monitor delivery in plain language." --- -The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) is an open standard that enables AI assistants to securely connect with external services. Lettr provides two MCP servers so AI agents can interact with your email infrastructure — sending messages, managing templates, configuring domains, and monitoring delivery — all through natural language. +The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) is an open standard that enables AI assistants to securely connect with external services. Lettr provides two MCP servers so AI agents can interact with your email infrastructure — sending messages, managing templates, configuring domains, running campaigns, managing your audience, and monitoring delivery — all through natural language. ## Why Use MCP? @@ -38,14 +38,17 @@ Lettr offers two MCP servers for different workflows. Both give AI assistants ac | **Transport** | HTTP/SSE | stdio | | **Supported clients** | Claude.ai, ChatGPT, Cursor, Claude Desktop, GitHub Copilot | Claude Code, Cursor, Claude Desktop | | **Best for** | Chat-based account management, monitoring, analytics | Developer workflows, coding assistants, CI environments | -| **Unique tools** | Analytics, email events, API logs | Domain CRUD, template delete, domain verification | +| **Tool count** | 77 | 69 | +| **Unique tools** | Analytics, template stats, email event browsing, API logs, sending health check, DNS diagnosis, API keys, tracking/inbound/storage domains, dedicated template send tool | Email event listing with bounce class and transmission filters, API health check, API key validation | | **Open source** | No | [Yes](https://github.com/lettr-com/lettr-mcp) | ### Which Should I Use? -**Use the remote server** if you want to manage your Lettr account from a web-based AI chat like Claude.ai or ChatGPT, or if you need analytics and event monitoring tools. The remote server authenticates through OAuth so you don't need to manage API keys. +Both servers cover the same core workflows — sending and scheduling email, templates, sending domains, webhooks, campaigns, and audience management. The choice mostly comes down to how you authenticate and where your AI assistant runs. -**Use the local server** if you want email capabilities inside your IDE or CLI during development. The local server is ideal for Claude Code and Cursor workflows where you need to send emails, manage templates, or configure domains as part of your coding process. +**Use the remote server** if you want to manage your Lettr account from a web-based AI chat like Claude.ai or ChatGPT, or if you need analytics, API logs, sending health checks, or DNS diagnosis. The remote server authenticates through OAuth so you don't need to manage API keys. + +**Use the local server** if you want email capabilities inside your IDE or CLI during development, authenticated with an API key you control. It's ideal for Claude Code and Cursor workflows, and you can limit what it can do with [restricted API keys](/learn/api-keys/permissions). Cursor and Claude Desktop work with both servers. The remote server uses OAuth (no API key needed), while the local server uses an API key and runs as a local process. You can use both simultaneously. @@ -58,14 +61,22 @@ Both MCP servers give AI assistants tools to work with your email infrastructure | Capability | Remote | Local | |---|---|---| | Send emails (direct and template-based) | Yes | Yes | -| List, view, create, and update templates | Yes | Yes | -| Delete templates and inspect merge tags | No | Yes | +| Schedule emails and cancel scheduled sends | Yes | Yes | +| View sent email history and delivery timelines | Yes | Yes | +| List, view, create, update, and delete templates | Yes | Yes | +| Inspect merge tags, template HTML, folders, and projects | Yes | Yes | | List sending domains | Yes | Yes | -| Create, verify, and delete domains | No | Yes | +| Create, verify, and delete sending domains | Yes | Yes | +| Diagnose sending domain DNS and check sending health | Yes | No | +| List tracking, inbound, and storage domains | Yes | No | +| List, inspect, create, update, and delete webhooks | Yes | Yes | +| List, send, schedule, and unschedule campaigns | Yes | Yes | +| Manage audience contacts, lists, topics, properties, and segments | Yes | Yes | | View analytics and template statistics | Yes | No | -| Browse email events and API logs | Yes | No | -| List API keys and webhooks | Yes | Yes | -| Inspect webhook details | No | Yes | +| Browse email events across all messages | Yes | Yes | +| Browse API request logs | Yes | No | +| List API keys | Yes | No | +| Check API health and validate the API key | No | Yes | ## Example Conversations diff --git a/learn/mcp/local-setup.mdx b/learn/mcp/local-setup.mdx index ee81522..a9a6902 100644 --- a/learn/mcp/local-setup.mdx +++ b/learn/mcp/local-setup.mdx @@ -113,29 +113,38 @@ The AI should respond with your verified sending domains. If you see domain info ## Available Tools -The local MCP server provides 14 tools for managing your email infrastructure: +The local MCP server provides 69 tools for managing your email infrastructure: ### Emails | Tool | Description | -|------|-------------| +|-|-| | `send-email` | Send a transactional email with HTML, plain text, templates, attachments, tracking, and personalization | +| `schedule-email` | Schedule an email for delivery up to 3 days ahead | +| `get-scheduled-email` | Get the state and details of a scheduled email | +| `cancel-scheduled-email` | Cancel a scheduled email before it is sent | +| `list-emails` | List recently sent emails | +| `get-email-detail` | Get the full delivery timeline of a sent email | +| `list-email-events` | List delivery, bounce, open, and click events across all sent messages | ### Templates | Tool | Description | -|------|-------------| -| `list-templates` | List email templates with pagination | +|-|-| +| `list-templates` | List email templates with pagination, filtered by project, purpose, or folder | | `get-template` | Get full template details including HTML content | -| `create-template` | Create a new template with HTML or visual editor JSON | +| `get-template-html` | Get a template's full rendered HTML and merge tags | +| `create-template` | Create a new transactional or campaign template with HTML or visual editor JSON | | `update-template` | Update template name and/or content (creates new version) | | `delete-template` | Permanently delete a template and all versions | | `get-merge-tags` | Discover merge tag variables a template expects | +| `list-folders` | List template folders with their purpose and template count | +| `list-projects` | List the projects available to your team | ### Domains | Tool | Description | -|------|-------------| +|-|-| | `list-domains` | List all sending domains and their verification status | | `create-domain` | Register a new sending domain | | `get-domain` | Get domain details with DNS records | @@ -145,9 +154,90 @@ The local MCP server provides 14 tools for managing your email infrastructure: ### Webhooks | Tool | Description | -|------|-------------| +|-|-| | `list-webhooks` | List all webhook configurations | | `get-webhook` | Get webhook details and delivery status | +| `create-webhook` | Create a webhook subscription | +| `update-webhook` | Update a webhook's name, URL, authentication, events, or enabled state | +| `delete-webhook` | Delete a webhook subscription | + +### Campaigns + +| Tool | Description | +|-|-| +| `list-campaigns` | List campaigns with engagement stats | +| `get-campaign` | Get campaign details, stats, and content | +| `list-campaign-events` | List engagement events for a campaign | +| `send-campaign` | Immediately send a draft campaign to its audience | +| `schedule-campaign` | Schedule or reschedule a campaign | +| `unschedule-campaign` | Cancel a scheduled send and return the campaign to draft | + +### Audience Contacts + +| Tool | Description | +|-|-| +| `list-audience-contacts` | List contacts filtered by search, status, list, or segment | +| `get-audience-contact` | Get a contact with its properties, lists, and topics | +| `create-audience-contact` | Create a contact, optionally with double opt-in | +| `bulk-create-audience-contacts` | Import up to 1000 contacts at once | +| `update-audience-contact` | Update a contact's email, status, or properties | +| `delete-audience-contact` | Permanently delete a contact | + +### Audience Lists + +| Tool | Description | +|-|-| +| `list-audience-lists` | List contact lists with pagination | +| `get-audience-list` | Get a list and its contact count | +| `create-audience-list` | Create a contact list | +| `update-audience-list` | Rename a contact list | +| `delete-audience-list` | Delete a contact list | +| `bulk-delete-audience-lists` | Delete up to 50 lists at once | +| `attach-contact-to-list` | Add a contact to a list | +| `detach-contact-from-list` | Remove a contact from a list | +| `bulk-attach-contacts-to-lists` | Add many contacts to many lists | +| `bulk-detach-contacts-from-lists` | Remove many contacts from many lists | + +### Audience Topics + +| Tool | Description | +|-|-| +| `list-audience-topics` | List subscription topics with pagination | +| `get-audience-topic` | Get a subscription topic | +| `create-audience-topic` | Create a subscription topic | +| `update-audience-topic` | Update a topic's name, description, or visibility | +| `delete-audience-topic` | Delete a subscription topic | +| `subscribe-contact-to-topic` | Subscribe a contact to a topic | +| `unsubscribe-contact-from-topic` | Unsubscribe a contact from a topic | +| `bulk-subscribe-contacts-to-topics` | Subscribe many contacts to many topics | +| `bulk-unsubscribe-contacts-from-topics` | Unsubscribe many contacts from many topics | + +### Audience Properties + +| Tool | Description | +|-|-| +| `list-audience-properties` | List custom contact properties | +| `get-audience-property` | Get a custom contact property | +| `create-audience-property` | Define a new custom contact property | +| `update-audience-property` | Update a property's fallback value | +| `delete-audience-property` | Delete a custom contact property | + +### Audience Segments + +| Tool | Description | +|-|-| +| `list-audience-segments` | List segments with pagination | +| `get-audience-segment` | Get a segment and its conditions | +| `create-audience-segment` | Create a segment from match conditions | +| `update-audience-segment` | Update a segment's name, list, or conditions | +| `delete-audience-segment` | Delete a segment | + +### System + +| Tool | Description | +|-|-| +| `health-check` | Check the Lettr API health status | +| `auth-check` | Validate the configured API key and return its team ID | For detailed parameter documentation, see the [Tools Reference](/learn/mcp/tools-reference). diff --git a/learn/mcp/setup.mdx b/learn/mcp/setup.mdx index 04c48df..09f68aa 100644 --- a/learn/mcp/setup.mdx +++ b/learn/mcp/setup.mdx @@ -152,65 +152,107 @@ The AI should respond with your verified sending domains. If you see domain info ## Available Actions -Once connected, your AI assistant gains access to a range of Lettr capabilities. These are organized into categories based on the type of operation. +Once connected, your AI assistant gains access to 77 Lettr tools. They are organized into categories based on the type of operation. See the [Tools Reference](/learn/mcp/tools-reference) for every parameter. ### Email Operations -For sending emails, your AI assistant can use `send_email` to compose and send messages with specified recipients, subject, and content, or `send_template_email` to send using a pre-built template with merge tag substitution. +For sending emails, your AI assistant can use `send_email` to compose and send messages, or `send_template_email` to send using a pre-built template with merge tag substitution. Emails can also be scheduled for later delivery and inspected after they go out. | Action | Description | -|--------|-------------| +|-|-| | `send_email` | Send an email with specified recipients, subject, and content | | `send_template_email` | Send an email using a pre-built template with merge tags | +| `schedule_email` | Schedule an email for delivery up to 3 days ahead | +| `get_scheduled_email`, `cancel_scheduled_email` | Inspect or cancel a scheduled email | +| `list_emails` | List recently sent emails | +| `get_email_detail` | View the full delivery timeline of a sent email | ### Template Management -Your AI assistant can work with email templates—listing, viewing, creating, and updating them. +Your AI assistant can work with email templates—listing, viewing, creating, updating, and deleting them—and discover the merge tags, folders, and projects they depend on. | Action | Description | -|--------|-------------| -| `list_templates` | List all email templates with metadata and merge tags | -| `get_template` | View a template's HTML content and configuration | -| `create_template` | Create a new Custom HTML email template | +|-|-| +| `list_templates` | List templates, optionally filtered by project, purpose, or folder | +| `get_template` | View a template's details, versions, and HTML content | +| `get_template_html` | Get a template's full rendered HTML | +| `get_merge_tags` | Discover which merge tags a template expects | +| `create_template` | Create a new Custom HTML template for transactional email or campaigns | | `update_template` | Update template content and create a new version | +| `delete_template` | Permanently delete a template and all its versions | +| `list_folders`, `list_projects` | Discover the folders and projects templates live in | + +### Campaigns + +The AI can review campaigns and their engagement, and send or schedule them. Sending and scheduling dispatch real emails to the campaign's audience, so confirm before approving these actions. + +| Action | Description | +|-|-| +| `list_campaigns`, `get_campaign` | View campaigns with engagement stats | +| `list_campaign_events` | View opens, clicks, bounces, and other events for a campaign | +| `send_campaign` | Send a draft campaign immediately | +| `schedule_campaign`, `unschedule_campaign` | Schedule a campaign or return it to draft | + +### Audience + +Your AI assistant can manage the contacts your campaigns are sent to, including lists, subscription topics, custom properties, and segments. + +| Action | Description | +|-|-| +| `list_audience_contacts`, `get_audience_contact`, `create_audience_contact`, `update_audience_contact`, `delete_audience_contact` | Manage individual contacts, including double opt-in | +| `bulk_create_audience_contacts` | Import up to 1000 contacts at once | +| `list_audience_lists`, `get_audience_list`, `create_audience_list`, `update_audience_list`, `delete_audience_list`, `bulk_delete_audience_lists` | Manage contact lists | +| `attach_contact_to_list`, `detach_contact_from_list`, `bulk_attach_contacts_to_lists`, `bulk_detach_contacts_from_lists` | Manage list memberships | +| `list_audience_topics`, `get_audience_topic`, `create_audience_topic`, `update_audience_topic`, `delete_audience_topic` | Manage subscription topics | +| `subscribe_contact_to_topic`, `unsubscribe_contact_from_topic`, `bulk_subscribe_contacts_to_topics`, `bulk_unsubscribe_contacts_from_topics` | Manage topic subscriptions | +| `list_audience_properties`, `get_audience_property`, `create_audience_property`, `update_audience_property`, `delete_audience_property` | Manage custom contact properties | +| `list_audience_segments`, `get_audience_segment`, `create_audience_segment`, `update_audience_segment`, `delete_audience_segment` | Manage segments built from match conditions | ### Analytics For insights into email performance, the AI can pull aggregate delivery metrics or per-template statistics. | Action | Description | -|--------|-------------| +|-|-| | `get_analytics` | View aggregate delivery analytics with daily breakdown | | `list_template_stats` | View per-template delivery statistics by campaign | ### Event Monitoring -To track what happens after emails are sent, the AI can use `browse_email_events` to view delivery events including deliveries, opens, clicks, and bounces. For debugging API integrations, `browse_api_logs` provides access to request history and response data. +To track what happens after emails are sent, the AI can use `browse_email_events` to view delivery events including deliveries, opens, clicks, and bounces. For debugging API integrations, `browse_api_logs` provides access to request history and response data, and `check_sending_health` summarizes your overall deliverability. | Action | Description | -|--------|-------------| +|-|-| | `browse_email_events` | View delivery events (delivered, opened, clicked, bounced, etc.) | | `browse_api_logs` | Inspect API request history and responses | +| `check_sending_health` | Get a single verdict on domains, delivery trends, and recent problems | ### Domain Management -Your AI assistant can check the status of your email infrastructure across all domain types. Each domain category has its own listing action that returns verification status and configuration details. +Your AI assistant can register, verify, and remove sending domains, diagnose DNS problems, and check the status of every domain type. | Action | Description | -|--------|-------------| +|-|-| | `list_sending_domains` | View sending domains with verification status | +| `get_domain` | View a sending domain's DNS records and verification details | +| `create_domain` | Register a new sending domain | +| `verify_domain` | Re-run DNS verification for a sending domain | +| `delete_domain` | Remove a sending domain | +| `diagnose_sending_domain_dns` | Explain why a sending domain is still failing verification | | `list_tracking_domains` | View tracking domains for branded links | | `list_inbound_domains` | View inbound email receiving domains | | `list_storage_domains` | View asset storage/CDN domains | ### Configuration -Beyond domains, the AI can inspect your account configuration including API keys and webhooks. For security, API keys are shown with prefixes only—never full values. +Beyond domains, the AI can inspect API keys and fully manage webhooks. For security, API keys are shown with prefixes only—never full values. | Action | Description | -|--------|-------------| +|-|-| | `list_api_keys` | View API keys (prefixes only, for security) | -| `list_webhooks` | View webhook endpoints and their configuration | +| `list_webhooks`, `get_webhook` | View webhook endpoints and their configuration | +| `create_webhook`, `update_webhook` | Create or change a webhook's URL, authentication, and events | +| `delete_webhook` | Delete a webhook subscription | ## Example Workflows @@ -291,7 +333,7 @@ See [Rate Limits](/api-reference/rate-limit) for detailed information. Run the MCP server locally with an API key - Full reference for all remote server tools + Full reference for all MCP tools across both servers Set up domains for sending emails diff --git a/learn/mcp/tools-reference.mdx b/learn/mcp/tools-reference.mdx index 3f9c486..eef3e72 100644 --- a/learn/mcp/tools-reference.mdx +++ b/learn/mcp/tools-reference.mdx @@ -1,41 +1,102 @@ --- title: Tools Reference -description: "Complete reference for every Lettr MCP tool for sending, templates, domains, analytics, and monitoring across the remote and local servers." +description: "Complete reference for every Lettr MCP tool for sending, templates, domains, campaigns, audience, analytics, and monitoring across the remote and local servers." --- -This reference documents every tool available through Lettr's MCP servers. Some tools are available on both the [remote](/learn/mcp/setup) and [local](/learn/mcp/local-setup) servers, while others are exclusive to one. +This reference documents every tool available through Lettr's MCP servers. The [remote server](/learn/mcp/setup) exposes 77 tools and the [local server](/learn/mcp/local-setup) exposes 69. Most tools are available on both; a few analytics, monitoring, and diagnostic tools are exclusive to the remote server, and a few event and system tools are exclusive to the local server. The remote server uses underscores in tool names (e.g., `send_email`) while the local server uses hyphens (e.g., `send-email`). The functionality is equivalent. + + The parameter tables below describe the remote server. The local server's parameters can differ in naming and shape — for example, it identifies audience and campaign resources with camelCase parameters such as `contactId` and `campaignId`, webhooks with `id`, and templates with `slug`. Your AI client always receives each server's exact input schema. + + ## Tool Availability | Category | Tool | Remote | Local | -|----------|------|--------|-------| +|-|-|-|-| | **Sending** | send_email | Yes | Yes | | **Sending** | send_template_email | Yes | — | +| **Sending** | schedule_email | Yes | Yes | +| **Sending** | get_scheduled_email | Yes | Yes | +| **Sending** | cancel_scheduled_email | Yes | Yes | +| **Email History** | list_emails | Yes | Yes | +| **Email History** | get_email_detail | Yes | Yes | +| **Email History** | list-email-events | — | Yes | | **Templates** | list_templates | Yes | Yes | | **Templates** | get_template | Yes | Yes | +| **Templates** | get_template_html | Yes | Yes | +| **Templates** | get_merge_tags | Yes | Yes | | **Templates** | create_template | Yes | Yes | | **Templates** | update_template | Yes | Yes | -| **Templates** | delete_template | — | Yes | -| **Templates** | get_merge_tags | — | Yes | -| **Domains** | list_sending_domains / list_domains | Yes | Yes | +| **Templates** | delete_template | Yes | Yes | +| **Templates** | list_folders | Yes | Yes | +| **Templates** | list_projects | Yes | Yes | +| **Domains** | list_sending_domains / list-domains | Yes | Yes | +| **Domains** | get_domain | Yes | Yes | +| **Domains** | create_domain | Yes | Yes | +| **Domains** | verify_domain | Yes | Yes | +| **Domains** | delete_domain | Yes | Yes | +| **Domains** | diagnose_sending_domain_dns | Yes | — | | **Domains** | list_tracking_domains | Yes | — | | **Domains** | list_inbound_domains | Yes | — | | **Domains** | list_storage_domains | Yes | — | -| **Domains** | create_domain | — | Yes | -| **Domains** | get_domain | — | Yes | -| **Domains** | delete_domain | — | Yes | -| **Domains** | verify_domain | — | Yes | +| **Campaigns** | list_campaigns | Yes | Yes | +| **Campaigns** | get_campaign | Yes | Yes | +| **Campaigns** | list_campaign_events | Yes | Yes | +| **Campaigns** | send_campaign | Yes | Yes | +| **Campaigns** | schedule_campaign | Yes | Yes | +| **Campaigns** | unschedule_campaign | Yes | Yes | +| **Audience: Contacts** | list_audience_contacts | Yes | Yes | +| **Audience: Contacts** | get_audience_contact | Yes | Yes | +| **Audience: Contacts** | create_audience_contact | Yes | Yes | +| **Audience: Contacts** | bulk_create_audience_contacts | Yes | Yes | +| **Audience: Contacts** | update_audience_contact | Yes | Yes | +| **Audience: Contacts** | delete_audience_contact | Yes | Yes | +| **Audience: Lists** | list_audience_lists | Yes | Yes | +| **Audience: Lists** | get_audience_list | Yes | Yes | +| **Audience: Lists** | create_audience_list | Yes | Yes | +| **Audience: Lists** | update_audience_list | Yes | Yes | +| **Audience: Lists** | delete_audience_list | Yes | Yes | +| **Audience: Lists** | bulk_delete_audience_lists | Yes | Yes | +| **Audience: Lists** | attach_contact_to_list | Yes | Yes | +| **Audience: Lists** | detach_contact_from_list | Yes | Yes | +| **Audience: Lists** | bulk_attach_contacts_to_lists | Yes | Yes | +| **Audience: Lists** | bulk_detach_contacts_from_lists | Yes | Yes | +| **Audience: Topics** | list_audience_topics | Yes | Yes | +| **Audience: Topics** | get_audience_topic | Yes | Yes | +| **Audience: Topics** | create_audience_topic | Yes | Yes | +| **Audience: Topics** | update_audience_topic | Yes | Yes | +| **Audience: Topics** | delete_audience_topic | Yes | Yes | +| **Audience: Topics** | subscribe_contact_to_topic | Yes | Yes | +| **Audience: Topics** | unsubscribe_contact_from_topic | Yes | Yes | +| **Audience: Topics** | bulk_subscribe_contacts_to_topics | Yes | Yes | +| **Audience: Topics** | bulk_unsubscribe_contacts_from_topics | Yes | Yes | +| **Audience: Properties** | list_audience_properties | Yes | Yes | +| **Audience: Properties** | get_audience_property | Yes | Yes | +| **Audience: Properties** | create_audience_property | Yes | Yes | +| **Audience: Properties** | update_audience_property | Yes | Yes | +| **Audience: Properties** | delete_audience_property | Yes | Yes | +| **Audience: Segments** | list_audience_segments | Yes | Yes | +| **Audience: Segments** | get_audience_segment | Yes | Yes | +| **Audience: Segments** | create_audience_segment | Yes | Yes | +| **Audience: Segments** | update_audience_segment | Yes | Yes | +| **Audience: Segments** | delete_audience_segment | Yes | Yes | | **Configuration** | list_api_keys | Yes | — | | **Configuration** | list_webhooks | Yes | Yes | -| **Configuration** | get_webhook | — | Yes | +| **Configuration** | get_webhook | Yes | Yes | +| **Configuration** | create_webhook | Yes | Yes | +| **Configuration** | update_webhook | Yes | Yes | +| **Configuration** | delete_webhook | Yes | Yes | | **Analytics** | get_analytics | Yes | — | | **Analytics** | list_template_stats | Yes | — | | **Monitoring** | browse_email_events | Yes | — | | **Monitoring** | browse_api_logs | Yes | — | +| **Monitoring** | check_sending_health | Yes | — | +| **System** | health-check | — | Yes | +| **System** | auth-check | — | Yes | --- @@ -43,7 +104,7 @@ This reference documents every tool available through Lettr's MCP servers. Some ### send_email -Send an email with plain text, HTML, or AMP HTML content. Supports merge tags for variable substitution. +Send an email with plain text, HTML, or AMP HTML content. Supports merge tags for variable substitution. The email goes out immediately. **Available on:** Remote, Local @@ -51,8 +112,8 @@ Send an email with plain text, HTML, or AMP HTML content. Supports merge tags fo |-----------|------|----------|-------------| | `from` | string | Yes | Sender email address. Must be from a verified sending domain. | | `to` | string | Yes | Recipient email address. | -| `subject` | string | Yes | Email subject line (max 998 characters). | -| `html` | string | No | HTML content. Supports merge tags like `{{variable_name}}`. | +| `subject` | string | Yes | Email subject line (max 998 characters). Supports merge tags. | +| `html` | string | No | HTML content. Supports merge tags like `{{variable_name}}`. At least one of `html`, `text`, or `amp_html` is required. | | `text` | string | No | Plain text content. Supports merge tags. | | `amp_html` | string | No | AMP HTML content for interactive emails. | | `from_name` | string | No | Display name for the sender. | @@ -60,28 +121,34 @@ Send an email with plain text, HTML, or AMP HTML content. Supports merge tags fo | `cc` | string | No | CC recipient email address. | | `bcc` | string | No | BCC recipient email address. | | `substitution_data` | object | No | Key-value pairs for merge tag substitution. | +| `idempotency_key` | string | No | 1–255 letters, digits, periods, underscores, or hyphens. Reuse the same key when retrying and the original result is returned instead of a second email being sent. Keys are kept for 24 hours. | **Example prompt:** "Send a welcome email to john@example.com from hello@mycompany.com with the subject 'Welcome to Acme'" + + On the local server, `send-email` also supports template-based sending via a `template_slug` parameter, multiple recipients, attachments, custom headers, tags, metadata, and tracking options. + + --- ### send_template_email -Send an email using a pre-defined template specified by its slug. +Send an email using a pre-defined template specified by its slug. The email goes out immediately. -**Available on:** Remote only +**Available on:** Remote only. On the local server, pass `template_slug` to `send-email` instead. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `from` | string | Yes | Sender email address. Must be from a verified sending domain. | | `to` | string | Yes | Recipient email address. | -| `subject` | string | Yes | Email subject line. Can include merge tags. | | `template_slug` | string | Yes | The slug of the template to use (e.g., `welcome-email`). | +| `subject` | string | No | Email subject line. Can include merge tags. Defaults to the template's own subject. | | `from_name` | string | No | Display name for the sender. | | `reply_to` | string | No | Reply-to email address. | | `project_id` | integer | No | Project ID where the template is located. Uses default project if omitted. | | `template_version` | integer | No | Specific version number. Uses active version if omitted. | | `substitution_data` | object | No | Key-value pairs for merge tag substitution. | +| `idempotency_key` | string | No | 1–255 letters, digits, periods, underscores, or hyphens. Reuse the same key when retrying to avoid sending the email twice. Keys are kept for 24 hours. | **Example prompt:** "Send the order-confirmation template to customer@example.com with order_id 12345" @@ -91,25 +158,144 @@ Send an email using a pre-defined template specified by its slug. --- +### schedule_email + +Schedule an email for future delivery. Accepts the same content as `send_email`, or a `template_slug`, plus a delivery time between 5 minutes and 3 days in the future. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `from` | string | Yes | Sender email address on a verified sending domain. | +| `to` | array | Yes | Recipient email addresses (1–50, counted together with `cc` and `bcc`). | +| `scheduled_at` | string | Yes | ISO 8601 UTC datetime (e.g., `2026-01-16T10:00:00Z`). Must be at least 5 minutes in the future and within 3 days. | +| `subject` | string | No | Subject line. Required unless `template_slug` is provided; overrides the template subject when both are given. | +| `html` | string | No | HTML content. At least one of `html`, `text`, or `template_slug` is required. | +| `text` | string | No | Plain text content. | +| `amp_html` | string | No | AMP HTML content. | +| `template_slug` | string | No | Template slug to use for the content. | +| `template_version` | integer | No | Specific template version. Defaults to the active version. | +| `project_id` | integer | No | Project containing the template. Defaults to the team's default project. | +| `substitution_data` | object | No | Merge tag values, e.g. `{"first_name": "John"}`. | +| `from_name` | string | No | Sender display name. | +| `reply_to` | string | No | Reply-to email address. | +| `reply_to_name` | string | No | Reply-to display name. | +| `cc` | array | No | CC addresses. | +| `bcc` | array | No | BCC addresses. | +| `tag` | string | No | Tag for analytics (max 64 characters). | +| `metadata` | object | No | Custom string metadata, e.g. `{"user_id": "123"}`. | +| `options` | object | No | Delivery options: `click_tracking`, `open_tracking`, `transactional`, `inline_css`, `perform_substitutions` (booleans). | + +Returns a transmission ID to use with `get_scheduled_email` and `cancel_scheduled_email`. + +**Example prompt:** "Schedule the weekly-digest template to team@example.com for tomorrow at 9:00 UTC" + +--- + +### get_scheduled_email + +Retrieve a scheduled email's state, scheduled time, sender, subject, recipients, and any events collected so far. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `transmission_id` | string | Yes | Transmission ID returned by `schedule_email`. | + +--- + +### cancel_scheduled_email + +Cancel a scheduled email before it is sent. This cannot be undone, and an email that has already started sending can no longer be cancelled. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `transmission_id` | string | Yes | Transmission ID returned by `schedule_email`. | + +--- + +## Email History + +### list_emails + +List recently sent emails with cursor pagination. Each entry includes the request ID, subject, recipient, and timestamp. Sent emails are kept for about 10 days. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `per_page` | integer | No | Results per page (1–100). Default: 25. | +| `cursor` | string | No | Pagination cursor from a previous response. | +| `recipients` | string | No | Only emails sent to this recipient address. | +| `from` | string | No | ISO 8601 start, inclusive. Defaults to 10 days ago. | +| `to` | string | No | ISO 8601 end, exclusive. Must be after `from`. | + +**Example prompt:** "What emails did we send to jane@example.com this week?" + +--- + +### get_email_detail + +Retrieve the full delivery timeline of a single sent email — its state (scheduled, delivered, bounced, failed), sender, subject, recipients, and every event. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `request_id` | string | Yes | Request ID returned by `send_email` or `send_template_email`. | +| `from` | string | No | ISO 8601 start, inclusive. Defaults to 10 days ago. | +| `to` | string | No | ISO 8601 end, exclusive. Defaults to now. Must be after `from`. | + +**Example prompt:** "Was the email I just sent to jane@example.com delivered?" + +--- + +### list-email-events + +List email events (delivery, bounce, click, open, and more) across all sent messages, filtered by event type, recipient, date range, transmission ID, or bounce class. + +**Available on:** Local only + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `events` | array | No | Filter by one or more event types. | +| `recipients` | array | No | Filter by one or more recipient email addresses. | +| `from` | string | No | ISO 8601 start date. Defaults to 10 days ago. | +| `to` | string | No | ISO 8601 end date. | +| `per_page` | integer | No | Results per page (1–100). Default: 25. | +| `cursor` | string | No | Pagination cursor. | +| `transmissions` | string | No | Filter by transmission ID (request ID). | +| `bounce_classes` | string | No | Comma-separated bounce classification codes. | + + + On the remote server, use `browse_email_events` for the same purpose. + + +--- + ## Template Management ### list_templates -Lists all email templates for your team with metadata including name, slug, editor type, version count, and merge tags. +Lists email templates for your team with metadata including name, slug, purpose, editor type, version count, and merge tags. **Available on:** Remote, Local | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `project_id` | integer | No | Filter templates by project. Returns templates from all projects if omitted. | +| `purpose` | string | No | Only return templates of this purpose: `transactional` or `campaign`. Use `campaign` to find templates a campaign can send. | +| `folder_id` | integer | No | Only return templates in this folder. Use `list_folders` to find folder IDs. | -**Example prompt:** "List all my email templates" or "What templates are in project 3?" +**Example prompt:** "List all my email templates" or "Which templates can I use for a campaign?" --- ### get_template -Gets detailed information about a specific template, including its HTML content and merge tags. +Gets detailed information about a specific template, including its purpose, versions, merge tags, and HTML content. **Available on:** Remote, Local @@ -127,6 +313,35 @@ Gets detailed information about a specific template, including its HTML content --- +### get_template_html + +Retrieves the full rendered HTML of a template's active version, along with its subject and merge tags. Unlike `get_template`, the HTML is never truncated. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `project_id` | integer | Yes | ID of the project containing the template. | +| `slug` | string | Yes | The template slug. | + +--- + +### get_merge_tags + +Retrieves the merge tag variables that a template expects, so you know which substitution data to provide when sending. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `slug` | string | Yes | The template slug. | +| `project_id` | integer | No | Project ID to find the template in. Defaults to the team's default project. | +| `version` | integer | No | Template version number. Defaults to the active version. | + +**Example prompt:** "What merge tags does the order-confirmation template expect?" + +--- + ### create_template Creates a new Custom HTML email template. @@ -136,20 +351,23 @@ Creates a new Custom HTML email template. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | Yes | Display name for the template. | -| `folder_id` | integer | Yes | The folder ID where the template will be created. | -| `slug` | string | No | URL-friendly identifier. Auto-generated from name if omitted. | +| `purpose` | string | No | `transactional` (default) or `campaign`. Cannot be changed after creation. | +| `folder_id` | integer | No | The folder to create the template in; its purpose must match. Defaults to the first folder of the template's purpose. | +| `slug` | string | No | URL-friendly identifier. Auto-generated from name if omitted. Must be unique within the project. | | `project_id` | integer | No | Project ID. Uses default project if omitted. | | `html` | string | No | Initial HTML content for the template. | | `subject` | string | No | Default subject line for the template. | -**Example prompt:** "Create a new template called 'Invoice Reminder' in folder 1 with a basic HTML layout" +**Example prompt:** "Create a campaign template called 'Spring Newsletter' with a basic HTML layout" - **This tool creates transactional templates only.** It does not take a `purpose`, so templates it creates cannot be picked by a [campaign](/learn/campaigns/introduction), and the mode cannot be changed afterwards. - - To create a marketing template from an AI agent, use the [remote MCP server](/learn/mcp/introduction) (`create-template` with `purpose: "campaign"`) or the API directly. To rescue one already created here, use the **Copy to Marketing** action in the app. + **Set `purpose` deliberately.** Only templates with the `campaign` purpose can be picked by a [campaign](/learn/campaigns/introduction), and the purpose cannot be changed afterwards. A newsletter created with the default `transactional` purpose has to be recreated — or rescued with the **Copy to Marketing** action in the app. + + The local `create-template` tool also accepts `purpose` and `folder_id`, and additionally takes a `json` parameter to create Topol editor templates. It does not accept `slug` or `subject`. + + --- ### update_template @@ -167,7 +385,7 @@ Updates a template's content and creates a new active version. Supports both Cus | `json` | string | No | Updated Topol editor JSON (for Topol templates). | - Provide either `template_id` or `template_slug` to identify the template. Each update creates a new version automatically. + Provide either `template_id` or `template_slug` to identify the template, and exactly one of `html` or `json`. Each update creates a new version automatically. **Example prompt:** "Update the welcome-email template with new HTML content" @@ -176,39 +394,50 @@ Updates a template's content and creates a new active version. Supports both Cus ### delete_template -Permanently deletes a template and all its versions. +Permanently deletes a template and all its versions. This is irreversible, and emails sent with the template's slug will fail afterwards. -**Available on:** Local only +**Available on:** Remote, Local | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `template_id` | integer | No | The numeric ID of the template. | -| `template_slug` | string | No | The slug of the template. | -| `project_id` | integer | No | Project ID to narrow the search when using a slug. | +| `slug` | string | Yes | The slug of the template to delete. | +| `project_id` | integer | No | Project ID to find the template in. Defaults to the team's default project. | **Example prompt:** "Delete the old-welcome-email template" --- -### get_merge_tags +### list_folders -Retrieves the merge tag variables that a template expects, so you know which substitution data to provide when sending. +Lists the template folders in a project. Each folder has a purpose (`transactional` or `campaign`) that templates inside it inherit. -**Available on:** Local only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `template_id` | integer | No | The numeric ID of the template. | -| `template_slug` | string | No | The slug of the template. | -| `project_id` | integer | No | Project ID to narrow the search when using a slug. | +|-|-|-|-| +| `project_id` | integer | No | Project ID. Defaults to the team's default project. | +| `purpose` | string | No | Only return folders with this purpose: `transactional` or `campaign`. | +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Folders per page (1–100). Default: 20. | -**Example prompt:** "What merge tags does the order-confirmation template expect?" +--- + +### list_projects + +Lists the projects owned by your team. Templates and folders live inside a project. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Projects per page (1–100). Default: 25. | --- ## Domain Management -### list_sending_domains / list_domains +### list_sending_domains / list-domains Lists all sending domains configured for your team with verification status, DNS configuration status, and whether they can be used for sending. @@ -220,6 +449,76 @@ Lists all sending domains configured for your team with verification status, DNS --- +### get_domain + +Gets full details of a sending domain, including CNAME, DKIM, DMARC, and SPF status, the DKIM record to publish, and any detected DNS provider. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `domain` | string | Yes | The domain name (e.g., `example.com`). | + +**Example prompt:** "Show me the DNS records for my sending domain" + +--- + +### create_domain + +Registers a new sending domain and returns the DKIM record to publish. The domain cannot send until its DNS records are verified. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `domain` | string | Yes | The domain name to register (e.g., `notifications.example.com`). | + +**Example prompt:** "Add notifications.example.com as a sending domain" + +--- + +### verify_domain + +Triggers DNS verification for a sending domain, stores the result, and returns the full validation report for DKIM, CNAME, DMARC, and SPF. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `domain` | string | Yes | The domain name to verify (e.g., `example.com`). | + +**Example prompt:** "Verify the DNS records for my sending domain" + +--- + +### delete_domain + +Removes a sending domain from your account. This is irreversible and stops all sending from that domain. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `domain` | string | Yes | The domain name to delete (e.g., `example.com`). | + +**Example prompt:** "Remove the old.example.com sending domain" + +--- + +### diagnose_sending_domain_dns + +Diagnoses why a sending domain is still pending or failing verification. Compares the expected DNS records against live DNS, detects common mistakes (wrong host, duplicate SPF records, proxied CNAMEs), and explains whether to wait for propagation or fix the records. It does not update the stored status. + +**Available on:** Remote only + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `domain` | string | Yes | The exact sending domain to diagnose (e.g., `mail.example.com`). Must belong to your team. | + +**Example prompt:** "Why is mail.example.com still not verified?" + +--- + ### list_tracking_domains Lists all tracking domains configured for your team. Tracking domains are used to track email opens and clicks with your own branded domain. @@ -252,171 +551,784 @@ Lists all email storage domains configured for your team. Storage domains serve --- -### create_domain +## Campaigns -Registers a new sending domain. +### list_campaigns -**Available on:** Local only +Lists campaigns for your team with embedded engagement stats. + +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `domain` | string | Yes | The domain name to register (e.g., `notifications.example.com`). | +|-|-|-|-| +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Campaigns per page (1–100). Default: 20. | +| `status` | string | No | Filter by status: `draft`, `scheduled`, `preparing`, `in_review`, `sending`, `sent`, `failed`. | -**Example prompt:** "Add notifications.example.com as a sending domain" +**Example prompt:** "List my draft campaigns" --- -### get_domain +### get_campaign -Gets detailed information about a sending domain, including its DNS records for SPF, DKIM, and DMARC setup. +Retrieves a single campaign, including engagement stats, rendered HTML content, and any per-language overrides. -**Available on:** Local only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `domain_id` | string | Yes | The ID of the domain to retrieve. | - -**Example prompt:** "Show me the DNS records for my sending domain" +|-|-|-|-| +| `campaign_id` | string | Yes | The campaign ID. | --- -### delete_domain +### list_campaign_events -Removes a sending domain from your account. +Lists engagement events for a campaign with cursor pagination. Keep paginating with `next_cursor` until it is null — a filtered page can be empty while more pages remain. -**Available on:** Local only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `domain_id` | string | Yes | The ID of the domain to delete. | +|-|-|-|-| +| `campaign_id` | string | Yes | The campaign ID. | +| `event_type` | string | No | Filter by event type: `injection`, `delivery`, `bounce`, `spam_complaint`, `open`, `click`, `list_unsubscribe`. | +| `email` | string | No | Filter by recipient email address. | +| `start_date` | string | No | Only events at or after this time (ISO 8601). | +| `end_date` | string | No | Only events at or before this time (ISO 8601, inclusive). | +| `limit` | integer | No | Max events per page (1–100). Default: 25. | +| `cursor` | string | No | Pagination cursor from a previous response. | -**Example prompt:** "Remove the old.example.com sending domain" +**Example prompt:** "Who clicked a link in the March newsletter campaign?" --- -### verify_domain +### send_campaign -Triggers DNS verification for a sending domain to check if SPF and DKIM records are properly configured. +Immediately sends a draft campaign to its whole audience. This is irreversible and outward-facing — emails cannot be recalled. The campaign must be a draft with a subject, sender, and content, and no pending delivery time. -**Available on:** Local only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `domain_id` | string | Yes | The ID of the domain to verify. | - -**Example prompt:** "Verify the DNS records for my sending domain" +|-|-|-|-| +| `campaign_id` | string | Yes | The ID of the draft campaign to send. | --- -## Configuration - -### list_api_keys +### schedule_campaign -Lists all API keys for your team, showing each key's name, key ID, prefix, permissions level, and IP whitelist configuration. +Schedules a draft campaign for future delivery, or reschedules an already-scheduled one. At the scheduled time it sends real emails to the campaign's whole audience. -**Available on:** Remote only +**Available on:** Remote, Local -**Parameters:** None +| Parameter | Type | Required | Description | +|-|-|-|-| +| `campaign_id` | string | Yes | The ID of the campaign to schedule. | +| `scheduled_at` | string | Yes | Future delivery time (ISO 8601, e.g., `2026-06-01T09:00:00+02:00`). A value without an offset is interpreted as UTC. | -**Example prompt:** "List my API keys and their permissions" +**Example prompt:** "Schedule the spring newsletter campaign for next Monday at 9am CET" --- -### list_webhooks +### unschedule_campaign -Lists all webhooks configured for your team, including target URL, enabled status, event types, authentication type, and delivery health indicators. +Cancels a scheduled send and returns the campaign to draft. Also clears a delivery time staged on a draft in the editor. **Available on:** Remote, Local -**Parameters:** None +| Parameter | Type | Required | Description | +|-|-|-|-| +| `campaign_id` | string | Yes | The ID of the scheduled campaign to unschedule. | --- -### get_webhook +## Audience: Contacts -Gets detailed information about a specific webhook, including its delivery status and configuration. +### list_audience_contacts -**Available on:** Local only +Lists audience contacts with pagination and optional filters. + +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `webhook_id` | string | Yes | The ID of the webhook to retrieve. | +|-|-|-|-| +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Contacts per page (1–100). Default: 20. | +| `search` | string | No | Search by email address or contact name. | +| `status` | string | No | Filter by status: `subscribed`, `unsubscribed`, `bounced`, `complained`, `unverified`. | +| `list_id` | string | No | Only return contacts in this list. | +| `segment_id` | string | No | Only return contacts matching this segment. | -**Example prompt:** "Show me the details for my delivery webhook" +**Example prompt:** "How many unsubscribed contacts are in my Newsletter list?" --- -## Analytics - -### get_analytics +### get_audience_contact -Returns aggregate email delivery analytics with a daily breakdown, including targeted, accepted, bounced, and opened counts. +Retrieves a single contact, including status, custom properties, and the lists and topics it belongs to. -**Available on:** Remote only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `from` | string | No | Start date in ISO 8601 format. Default: 7 days ago. Max 90 days back. | -| `to` | string | No | End date in ISO 8601 format. Default: now. | - -**Example prompt:** "Show me my email delivery stats for the last 30 days" +|-|-|-|-| +| `contact_id` | string | Yes | The contact ID. | --- -### list_template_stats +### create_audience_contact -Lists templates with delivery statistics broken down by campaign, including sent, delivered, opened, and bounced counts. +Creates a single audience contact. Fails if the email already exists for your team. See [Double Opt-In](/learn/audience/double-opt-in) for the confirmation flow. -**Available on:** Remote only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `project_id` | integer | No | Filter by project. Returns stats for all projects if omitted. | -| `from` | string | No | Start date in ISO 8601 format. Default: 30 days ago. | -| `to` | string | No | End date in ISO 8601 format. Default: now. | - -**Example prompt:** "Which of my templates has the best open rate this month?" +|-|-|-|-| +| `email` | string | Yes | Contact email address. | +| `list_id` | string | No | List to add the contact to. | +| `properties` | object | No | Custom property values, each a string (max 1000 characters). Keys must match properties defined for your team. | +| `double_opt_in` | object | No | Double opt-in configuration with `from`, `subject`, `template_slug`, and `redirect_url` (all required) and optional `from_name`. The contact stays unverified until they confirm. | --- -## Monitoring - -### browse_email_events +### bulk_create_audience_contacts -Browse email delivery events with optional filtering and pagination. +Creates up to 1000 contacts in one call. Provide exactly one of `emails` or `contacts`. The call can partially succeed — invalid rows are skipped and reported in the result. -**Available on:** Remote only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `limit` | integer | No | Number of events to return (10, 25, 50, or 100). Default: 25. | -| `recipient` | string | No | Filter by recipient email address. Supports partial matching. | -| `from` | string | No | Start date/time in ISO 8601 format. | -| `to` | string | No | End date/time in ISO 8601 format. | -| `event_type` | string | No | Filter by event type: `delivery`, `injection`, `open`, `initial_open`, `click`, `bounce`, `out_of_band`, `delay`, `spam_complaint`, `policy_rejection`, `generation_rejection`, `generation_failure`, `list_unsubscribe`, `link_unsubscribe`, `amp_open`, `amp_click` | -| `cursor` | string | No | Pagination cursor from a previous response. | +|-|-|-|-| +| `emails` | array | No | Flat list of email addresses (max 1000). Mutually exclusive with `contacts`. | +| `contacts` | array | No | One row per contact (max 1000), each with `email` and optional `properties`, `list_ids`, and `topics`. Mutually exclusive with `emails`. | +| `list_id` | string | No | Single list ID applied to the whole batch. | +| `list_ids` | array | No | List IDs applied to every contact (max 50). | +| `topics` | array | No | Topic subscriptions (`id` and `subscription`: `opt_in` or `opt_out`) applied to every contact (max 50). | +| `properties` | object | No | Property values applied to every contact. | +| `update_existing` | boolean | No | Merge submitted properties into contacts that already exist. Default: `false`. | -**Example prompt:** "Show me the last 10 bounced emails" or "What emails were sent to john@example.com today?" +**Example prompt:** "Import these 200 addresses into the Beta Testers list" --- -### browse_api_logs +### update_audience_contact -Browse API request logs with optional filtering and pagination. +Updates a contact. Only provided fields change. -**Available on:** Remote only +**Available on:** Remote, Local | Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `limit` | integer | No | Number of log entries (1, 20, 50, or 100). Default: 20. | -| `from` | string | No | Start date/time in ISO 8601 format. Max 30 days back. Default: 7 days ago. | -| `to` | string | No | End date/time in ISO 8601 format. Default: now. | -| `api_key_name` | string | No | Filter by API key name (case-insensitive). | -| `user_agent` | string | No | Filter by client type: `php`, `curl`, `nodejs`, `python`, `ruby`, `go`, `rust`, `java`, `dotnet`, `smtp`, `other` | -| `status` | integer | No | Filter by HTTP status code (e.g., 200, 400, 500). | -| `cursor` | string | No | Pagination cursor from a previous response. | +|-|-|-|-| +| `contact_id` | string | Yes | The contact ID to update. | +| `email` | string | No | New email address (must remain unique within the team). | +| `status` | string | No | New status: `subscribed` or `unsubscribed`. | +| `properties` | object | No | Partial property update. A property set to `null` is removed from the contact. | -**Example prompt:** "Show me failed API requests from the last 24 hours" or "Were there any 500 errors today?" +--- + +### delete_audience_contact + +Deletes a contact and removes it from all lists and topics. This is irreversible. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_id` | string | Yes | The contact ID to delete. | + +--- + +## Audience: Lists + +### list_audience_lists + +Lists your team's audience lists with pagination. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Lists per page (1–100). Default: 20. | + +--- + +### get_audience_list + +Retrieves a single audience list, including its current contact count. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `list_id` | string | Yes | The list ID. | + +--- + +### create_audience_list + +Creates a new audience list. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `name` | string | Yes | List name (max 255 characters, unique within the team). | + +--- + +### update_audience_list + +Renames an audience list. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `list_id` | string | Yes | The list ID to update. | +| `name` | string | Yes | New list name (unique within the team). | + +--- + +### delete_audience_list + +Deletes an audience list and detaches its contacts. This is irreversible; the contacts themselves are kept. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `list_id` | string | Yes | The list ID to delete. | + +--- + +### bulk_delete_audience_lists + +Deletes up to 50 audience lists in one call. This is irreversible. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `list_ids` | array | Yes | 1–50 list IDs to delete. | + +--- + +### attach_contact_to_list + +Adds a single contact to a single list. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_id` | string | Yes | The contact ID. | +| `list_id` | string | Yes | The list ID. | + +--- + +### detach_contact_from_list + +Removes a single contact from a single list. The contact itself is kept. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_id` | string | Yes | The contact ID. | +| `list_id` | string | Yes | The list ID. | + +--- + +### bulk_attach_contacts_to_lists + +Attaches every given contact to every given list. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_ids` | array | Yes | Contact IDs to attach (max 1000). | +| `list_ids` | array | Yes | List IDs to attach the contacts to (max 50). | + +--- + +### bulk_detach_contacts_from_lists + +Detaches every given contact from every given list, removing many memberships at once. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_ids` | array | Yes | Contact IDs to detach (max 1000). | +| `list_ids` | array | Yes | List IDs to detach the contacts from (max 50). | + +--- + +## Audience: Topics + +### list_audience_topics + +Lists your team's subscription topics with pagination. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Topics per page (1–100). Default: 20. | + +--- + +### get_audience_topic + +Retrieves a single subscription topic. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `topic_id` | string | Yes | The topic ID. | + +--- + +### create_audience_topic + +Creates a subscription topic. See [Topics](/learn/audience/topics). + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `name` | string | Yes | Topic name (max 255 characters). | +| `description` | string | No | Topic description (max 1000 characters). | +| `default_subscription` | string | No | `opt_in` (default) or `opt_out`. Cannot be changed later. | +| `visibility` | string | No | `private` (default) or `public`. Public topics are shown on preference pages. | + +--- + +### update_audience_topic + +Updates a subscription topic. Only provided fields change; `default_subscription` cannot be changed. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `topic_id` | string | Yes | The topic ID to update. | +| `name` | string | No | New topic name. | +| `description` | string | No | New description. Pass an empty string to clear it. | +| `visibility` | string | No | New visibility: `private` or `public`. | + +--- + +### delete_audience_topic + +Deletes a subscription topic and all contact subscriptions to it. This is irreversible. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `topic_id` | string | Yes | The topic ID to delete. | + +--- + +### subscribe_contact_to_topic + +Subscribes a single contact to a single topic. Subscribing an already subscribed contact has no effect. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_id` | string | Yes | The contact ID. | +| `topic_id` | string | Yes | The topic ID. | + +--- + +### unsubscribe_contact_from_topic + +Unsubscribes a single contact from a single topic. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_id` | string | Yes | The contact ID. | +| `topic_id` | string | Yes | The topic ID. | + +--- + +### bulk_subscribe_contacts_to_topics + +Subscribes every given contact to every given topic. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_ids` | array | Yes | Contact IDs to subscribe (max 1000). | +| `topic_ids` | array | Yes | Topic IDs to subscribe the contacts to (max 50). | + +--- + +### bulk_unsubscribe_contacts_from_topics + +Unsubscribes every given contact from every given topic, dropping many subscriptions at once. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `contact_ids` | array | Yes | Contact IDs to unsubscribe (max 1000). | +| `topic_ids` | array | Yes | Topic IDs to unsubscribe the contacts from (max 50). | + +--- + +## Audience: Properties + +### list_audience_properties + +Lists the custom contact properties defined for your team, with pagination. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Properties per page (1–100). Default: 20. | + +--- + +### get_audience_property + +Retrieves a single custom contact property. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `property_id` | string | Yes | The property ID. | + +--- + +### create_audience_property + +Defines a new custom contact property. The name and type cannot be changed later. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `name` | string | Yes | Property name. Must start with a lowercase letter and contain only lowercase letters, numbers, and underscores. | +| `type` | string | Yes | Value type: `string`, `number`, `boolean`, `date`, or `json`. | +| `fallback_value` | string | No | Default value used when a contact has no value set (max 255 characters). | +| `purpose` | string | No | Special purpose of the property: `communication_language`. Remote only. | + +--- + +### update_audience_property + +Updates a property's fallback value or purpose. Only provided fields change. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `property_id` | string | Yes | The property ID to update. | +| `fallback_value` | string | No | New fallback value (max 255 characters). Pass an empty string to clear it. | +| `purpose` | string | No | New purpose: `communication_language`. Pass an empty string to clear it. Remote only. | + +--- + +### delete_audience_property + +Deletes a custom contact property and removes its value from every contact. This is irreversible. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `property_id` | string | Yes | The property ID to delete. | + +--- + +## Audience: Segments + +### list_audience_segments + +Lists audience segments with pagination. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `page` | integer | No | Page number, starting at 1. | +| `per_page` | integer | No | Segments per page (1–100). Default: 20. | +| `list_id` | string | No | Only return segments restricted to this list. | + +--- + +### get_audience_segment + +Retrieves a single segment, including its condition groups and cached contact count. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `segment_id` | string | Yes | The segment ID. | + +--- + +### create_audience_segment + +Creates a segment defined by match conditions. See [Segments](/learn/audience/segments). + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `name` | string | Yes | Segment name (max 255 characters). | +| `conditions` | object | Yes | Match rules in the shape `{"groups": [{"conditions": [...]}]}`, where each condition has a `field`, `operator`, and `value`. Groups are ANDed; conditions within a group are ORed. Fields: `email`, `status`, `created_at`, `properties.`, `topics`, `lists`. | +| `list_id` | string | No | Restrict the segment to a single list. | + +**Example prompt:** "Create a segment of subscribed contacts whose country property is Germany" + +--- + +### update_audience_segment + +Updates a segment. Only provided fields change; providing `conditions` replaces the entire condition set. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `segment_id` | string | Yes | The segment ID to update. | +| `name` | string | No | New segment name. | +| `list_id` | string | No | New list restriction. Pass an empty string to remove it. | +| `conditions` | object | No | Replacement match rules, in the same shape as `create_audience_segment`. | + +--- + +### delete_audience_segment + +Deletes a segment. This is irreversible; contacts themselves are not deleted. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `segment_id` | string | Yes | The segment ID to delete. | + +--- + +## Configuration + +### list_api_keys + +Lists all API keys for your team, showing each key's name, key ID, prefix, permissions level, and IP whitelist configuration. + +**Available on:** Remote only + +**Parameters:** None + +**Example prompt:** "List my API keys and their permissions" + +--- + +### list_webhooks + +Lists all webhooks configured for your team, including ID, target URL, enabled status, event types, authentication type, and delivery health indicators. + +**Available on:** Remote, Local + +**Parameters:** None + +--- + +### get_webhook + +Gets detailed information about a specific webhook, including its enabled state, event types, auth type, and last delivery timestamps. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `webhook_id` | string | Yes | The webhook ID (a UUID, as returned by `list_webhooks`). | + +**Example prompt:** "Show me the details for my delivery webhook" + +--- + +### create_webhook + +Creates a new webhook subscription. See [Webhooks](/learn/webhooks/introduction) and [Event Types](/learn/webhooks/event-types). + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `name` | string | Yes | Human-readable name (max 24 characters). | +| `url` | string | Yes | HTTPS URL that receives webhook POSTs. | +| `auth_type` | string | Yes | Authentication scheme: `none`, `basic`, or `oauth2`. | +| `events_mode` | string | Yes | `all` to receive every event, or `selected` to subscribe only to `events`. | +| `events` | array | No | Event types, required when `events_mode` is `selected`. Uses fully-prefixed names, e.g. `message.delivery`, `engagement.open`. | +| `auth_username` | string | No | Basic auth username (required when `auth_type` is `basic`). | +| `auth_password` | string | No | Basic auth password (required when `auth_type` is `basic`). | +| `oauth_client_id` | string | No | OAuth2 client ID (required when `auth_type` is `oauth2`). | +| `oauth_client_secret` | string | No | OAuth2 client secret (required when `auth_type` is `oauth2`). | +| `oauth_token_url` | string | No | OAuth2 token URL (required when `auth_type` is `oauth2`). | + +**Example prompt:** "Create a webhook named 'Bounces' that sends bounce events to https://example.com/hooks/lettr" + +--- + +### update_webhook + +Updates an existing webhook. Only provided fields change; provide at least one of `name`, `url`, `auth_type`, `events`, or `active`. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `webhook_id` | string | Yes | The webhook ID to update. | +| `name` | string | No | New name (max 24 characters). | +| `url` | string | No | New delivery URL. | +| `auth_type` | string | No | New authentication scheme: `none`, `basic`, or `oauth2`. Required when changing any credential. | +| `auth_username` | string | No | Basic auth username (required when `auth_type` is `basic`). | +| `auth_password` | string | No | Basic auth password (required when `auth_type` is `basic`). | +| `oauth_client_id` | string | No | OAuth2 client ID (required when `auth_type` is `oauth2`). | +| `oauth_client_secret` | string | No | OAuth2 client secret (required when `auth_type` is `oauth2`). | +| `oauth_token_url` | string | No | OAuth2 token URL (required when `auth_type` is `oauth2`). | +| `events` | array | No | Replacement event subscription list, using fully-prefixed names. | +| `active` | boolean | No | Enable or disable the webhook. | + +--- + +### delete_webhook + +Deletes a webhook subscription. This is irreversible, and event delivery to its URL stops immediately. + +**Available on:** Remote, Local + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `webhook_id` | string | Yes | The webhook ID to delete. | + +--- + +## Analytics + +### get_analytics + +Returns aggregate email delivery analytics with a daily breakdown, including targeted, accepted, bounced, and opened counts. + +**Available on:** Remote only + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `from` | string | No | Start date in ISO 8601 format. Default: 7 days ago. Max 90 days back. | +| `to` | string | No | End date in ISO 8601 format. Default: now. | + +**Example prompt:** "Show me my email delivery stats for the last 30 days" + +--- + +### list_template_stats + +Lists templates with delivery statistics broken down by campaign, including sent, delivered, opened, and bounced counts. + +**Available on:** Remote only + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `project_id` | integer | No | Filter by project. Returns stats for all projects if omitted. | +| `from` | string | No | Start date in ISO 8601 format. Default: 30 days ago. | +| `to` | string | No | End date in ISO 8601 format. Default: now. | + +**Example prompt:** "Which of my templates has the best open rate this month?" + +--- + +## Monitoring + +### browse_email_events + +Browse email delivery events with optional filtering and pagination. Message events are retained for about 10 days. + +**Available on:** Remote only + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `limit` | integer | No | Number of events to return (10, 25, 50, or 100). Default: 25. | +| `recipient` | string | No | Filter by recipient email address. Supports partial matching. | +| `from` | string | No | Start date/time in ISO 8601 format. Dates older than the retention window are pulled forward. | +| `to` | string | No | End date/time in ISO 8601 format. | +| `event_type` | string | No | Filter by event type: `injection`, `delivery`, `bounce`, `delay`, `out_of_band`, `spam_complaint`, `policy_rejection`, `click`, `open`, `initial_open`, `amp_click`, `amp_open`, `amp_initial_open`, `generation_failure`, `generation_rejection`, `list_unsubscribe`, `link_unsubscribe` | +| `cursor` | string | No | Pagination cursor from a previous response. | + +**Example prompt:** "Show me the last 10 bounced emails" or "What emails were sent to john@example.com today?" + + + On the local server, use `list-email-events` for the same purpose. + + +--- + +### browse_api_logs + +Browse API request logs with optional filtering and pagination. + +**Available on:** Remote only + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `limit` | integer | No | Number of log entries (1, 20, 50, or 100). Default: 20. | +| `from` | string | No | Start date/time in ISO 8601 format. Max 30 days back. Default: 7 days ago. | +| `to` | string | No | End date/time in ISO 8601 format. Default: now. | +| `api_key_name` | string | No | Filter by API key name (case-insensitive). | +| `user_agent` | string | No | Filter by client type. Lettr SDKs: `lettr-php`, `lettr-node`, `lettr-laravel`, `lettr-java`, `lettr-go`, `lettr-python`, `lettr-rust`, `lettr-wordpress`, `lettr-mcp`, `lettr-n8n`. Generic clients: `php`, `curl`, `node`, `python`, `ruby`, `go`, `rust`, `java`, `dotnet`, `smtp`, `other` | +| `status` | integer | No | Filter by HTTP status code (e.g., 200, 400, 500). | +| `cursor` | string | No | Pagination cursor from a previous response. | + +**Example prompt:** "Show me failed API requests from the last 24 hours" or "Were there any 500 errors today?" + +--- + +### check_sending_health + +Gives a single overview of your team's sending health — domain verification, delivery trends, bounce and complaint rates, and the most common recent problems — with a verdict of healthy, needs attention, or at risk. + +**Available on:** Remote only + +| Parameter | Type | Required | Description | +|-|-|-|-| +| `days` | integer | No | Number of days to look back. Default: 7. Delivery trends cover up to 90 days; recent problem samples reach back about 10 days. | + +**Example prompt:** "Is anything wrong with my email delivery?" + +--- + +## System + +### health-check + +Checks the Lettr API health status and returns a server timestamp. + +**Available on:** Local only + +**Parameters:** None + +--- + +### auth-check + +Validates the configured API key and returns the associated team ID. Useful for confirming the local server is set up correctly. + +**Available on:** Local only + +**Parameters:** None ---