Agents don't read documentation
They read tool schemas. MCP gives every endpoint a strict, typed JSON-Schema definition that agents can introspect and call without prompting tricks.
pulsemail exposes a Mnemo Connect MCP endpoint. Your agents see every list, every subscriber, every template, and can send campaigns on instruction. No glue code. No webhook duct-tape.
Available on the Business tierThree things make MCP the right surface for campaign work — better than scripts that wrap REST and parse JSON for you.
They read tool schemas. MCP gives every endpoint a strict, typed JSON-Schema definition that agents can introspect and call without prompting tricks.
Instead of bash that wraps curl, parses fields, retries on 429, and logs to a sidecar — agents call typed tools natively. Idempotency, retries, and quotas are handled inside the MCP layer.
pulsemail MCP is registered in Mnemo Connect, alongside your other agents (mnemo, aigramm, listing.company tools). One connection, every product.
From key generation to your first agent call. Bring your own Mnemo Connect daemon — or use the bundled one.
Go to /admin/settings#api and click Generate MCP key. Choose scopes (read / write / send). Copy the mcp_live_… string — it's shown once.
# Settings → API → Generate MCP key
mcp_live_3f8b2c9e1a4d5670
Register the endpoint with your local Mnemo Connect daemon. The handshake exchanges capabilities and caches the tool schema.
mnemo connect mcp:wss://send.blun.ai/mcp \ --key mcp_live_3f8b2c9e1a4d5670 \ --alias pulsemail
Call any tool by name. Your agent gets the same view it would have in the dashboard — but as typed structured data.
mnemo call pulsemail.list_lists # → {"lists": [{"id": "lst_8a7b…", "name": "Newsletter", "subscribers": 12480}]}
Every tool has a typed argument schema and a typed return shape. Click any row to expand the JSON-Schema details and a working call example.
| Field | Type | Description |
|---|---|---|
| limit | integer | Page size, 1–200. Default 50. |
| cursor | string | Opaque pagination cursor returned by the previous call. |
| Field | Type | Description |
|---|---|---|
| lists | List[] | Array of list objects with id, name, subscribers, created_at. |
| next_cursor | string|null | Pass to the next call, or null if last page. |
mnemo call pulsemail.list_lists --args '{"limit": 20}' // → response { "lists": [ { "id": "lst_8a7b3c", "name": "Newsletter", "subscribers": 12480 }, { "id": "lst_4f9e1d", "name": "VIP buyers", "subscribers": 312 } ], "next_cursor": "eyJpZCI6Imxz..." }
| Field | Type | Description |
|---|---|---|
| list_id* | string | Required. Target list identifier. |
| status | enum | One of subscribed, unsubscribed, bounced, pending. |
| limit | integer | Page size, 1–200. Default 50. |
| cursor | string | Opaque pagination cursor. |
| Field | Type | Description |
|---|---|---|
| subscribers | Subscriber[] | Each entry has id, email, status, fields, tags, consent_at. |
| next_cursor | string|null | Pass to the next call. |
mnemo call pulsemail.list_subscribers --args '{ "list_id": "lst_8a7b3c", "status": "subscribed", "limit": 100 }'
| Field | Type | Description |
|---|---|---|
| status | enum | draft | scheduled | sending | sent | cancelled. |
| since | iso-8601 | Only return campaigns created after this timestamp. |
| limit | integer | Page size, 1–200. Default 50. |
| cursor | string | Opaque pagination cursor. |
| Field | Type | Description |
|---|---|---|
| campaigns | Campaign[] | Each has id, subject, status, list_id, sent_at, recipients. |
| next_cursor | string|null | Pagination continuation token. |
mnemo call pulsemail.list_campaigns --args '{ "status": "sent", "since": "2026-04-01T00:00:00Z" }'
| Field | Type | Description |
|---|---|---|
| campaign_id* | string | Required. Campaign to inspect. |
| Field | Type | Description |
|---|---|---|
| sent | integer | Total messages dispatched. |
| opens | integer | Unique open count. |
| clicks | integer | Unique click count. |
| ctr | float | Click-through rate, 0.0–1.0. |
| geographic | GeoBucket[] | Per-country opens with ISO-3166 codes. |
| devices | DeviceBucket[] | Desktop / mobile / tablet split. |
| heatmap | HourCell[24×7] | 2D grid of opens by hour of week. |
mnemo call pulsemail.get_campaign_analytics --args '{ "campaign_id": "cmp_61a8e9d3" }' // → response (truncated) { "sent": 12480, "opens": 5621, "clicks": 912, "ctr": 0.073 }
| Field | Type | Description |
|---|---|---|
| category | string | e.g. newsletter, welcome, announcement, transactional. |
| limit | integer | Page size, 1–200. |
| Field | Type | Description |
|---|---|---|
| templates | Template[] | id, name, category, variables[], preview_url. |
mnemo call pulsemail.list_templates --args '{"category": "newsletter"}'
| Field | Type | Description |
|---|---|---|
| list_id* | string | Required. Target list. |
| email* | string | Required. Validated against RFC 5322 + DNS MX. |
| fields | object | Custom fields keyed by name (e.g. {"first_name": "Anna"}). |
| tags | string[] | Tags to apply on creation. |
| consent_source* | string | Required. e.g. checkout-form, concierge-flow, imported-csv. |
| Field | Type | Description |
|---|---|---|
| subscriber_id | string | New subscriber identifier. |
| status | enum | subscribed | pending (if double opt-in is enabled). |
mnemo call pulsemail.add_subscriber --args '{ "list_id": "lst_8a7b3c", "email": "anna@example.eu", "fields": {"first_name": "Anna"}, "tags": ["happy-customer"], "consent_source": "concierge-flow" }'
| Field | Type | Description |
|---|---|---|
| subscriber_id* | string | Required. |
| tags* | string[] | Required. Existing tags are preserved unless prefixed with -. |
| Field | Type | Description |
|---|---|---|
| ok | boolean | true on success. |
mnemo call pulsemail.tag_subscriber --args '{ "subscriber_id": "sub_6f3e9a", "tags": ["vip", "-trial"] }'
| Field | Type | Description |
|---|---|---|
| kind* | enum | Required. One of hero, text, button, image, spacer, quote, divider. |
| props* | object | Required. Schema depends on kind. e.g. {"headline": "...", "image_url": "..."}. |
| Field | Type | Description |
|---|---|---|
| block_id | string | Reusable identifier. |
| html | string | Rendered MJML-compiled HTML for preview. |
mnemo call pulsemail.build_block --args '{ "kind": "hero", "props": { "headline": "Welcome aboard", "subhead": "A short note from the team" } }'
| Field | Type | Description |
|---|---|---|
| list_id | string | Target list. Either this or segment_id is required. |
| segment_id | string | Target segment within a list. |
| template_id | string | Optional. If set, blocks is ignored. |
| blocks | Block[] | Inline block definitions. Mutually exclusive with template_id. |
| subject* | string | Required. Subject line. Personalisation tokens supported. |
| from* | object | Required. {"email": "...", "name": "..."} from a verified sender domain. |
| Field | Type | Description |
|---|---|---|
| campaign_id | string | New campaign identifier. |
| draft | boolean | Always true; sends require schedule_send or send_campaign. |
mnemo call pulsemail.compose_campaign --args '{ "list_id": "lst_8a7b3c", "template_id": "tpl_monthly_v3", "subject": "May highlights — handpicked for {{first_name}}", "from": {"email": "team@blun.ai", "name": "BLUN team"} }'
| Field | Type | Description |
|---|---|---|
| campaign_id* | string | Required. Draft to schedule. |
| send_at_iso | iso-8601 | UTC timestamp. Ignored when optimize: true. |
| optimize | boolean | If true, the engine picks the best slot from the open heatmap. |
| Field | Type | Description |
|---|---|---|
| scheduled_at | iso-8601 | Confirmed dispatch time. |
| estimated_recipients | integer | Snapshot of audience size at schedule time. |
mnemo call pulsemail.schedule_send --args '{ "campaign_id": "cmp_61a8e9d3", "send_at_iso": "2026-05-12T07:30:00Z" }'
| Field | Type | Description |
|---|---|---|
| campaign_id* | string | Required. Draft to send now. |
| Field | Type | Description |
|---|---|---|
| send_id | string | Identifier for the dispatch job — used by cancel_send. |
| queued_recipients | integer | Count of validated, deduplicated addresses queued. |
mnemo call pulsemail.send_campaign --args '{ "campaign_id": "cmp_61a8e9d3" }'
| Field | Type | Description |
|---|---|---|
| send_id* | string | Required. Returned by send_campaign or schedule_send. |
| Field | Type | Description |
|---|---|---|
| ok | boolean | true if cancellation took effect before queued messages flushed. |
mnemo call pulsemail.cancel_send --args '{ "send_id": "snd_42ab17f0" }'
These are full-loop examples that combine multiple tools — not toy snippets. Each one runs as a Mnemo Connect skill.
An editorial agent runs once a month. Reads which tags performed best, drafts a newsletter from those case studies, hits the optimised slot, posts a 1-line summary back to Mnemo.
list_campaigns with since: -30dget_campaign_analytics for top 3 performerscompose_campaign with a curated block listschedule_send with optimize: true for Tuesday windowbrief_drop with the campaign id + snapshotEvery Sunday, identifies subscribers who haven't opened in 60 days. Builds a personalised re-engagement note per segment. Schedules a Monday 10:00 wave.
list_subscribers with status: subscribedlast_open_at < 60 daystag_subscriber → cool or warmcompose_campaign per segmentschedule_send for next Monday 10:00 localWhen the concierge agent closes a deal in listing.company, the buyer is added to a "happy-customer" segment in pulsemail and the welcome flow is triggered.
add_subscriber with consent_source: concierge-flowtag_subscriber → happy-customercompose_campaign from welcome-v3 templatesend_campaign immediatelyThe pulsemail MCP server is registered as pulsemail-65er in the Mnemo Connect tree. Mention it in your other agents' channels — for example @pulsemail-65er please send the May digest — and Mnemo will route the request via the brief queue.
This means an editorial agent can hand off to pulsemail without holding the MCP key itself, and the audit log captures both sides of the handoff.
Learn about Mnemo ConnectMCP keys carry the same trust model as REST keys. Three layers: scope, rotation, audit.
Choose read, write, or send per key. Sending a campaign requires the send scope explicitly.
Keys rotate from /admin/settings#api. Old key remains valid for 60 minutes during cutover, then revoked.
Every MCP call is recorded with timestamp, key id, tool name, args hash, and result code. Available at /admin/settings#audit.
Generate a key. Run one mnemo connect command. Watch your agents send.