API and MCP
MCP server and REST API
EmailPal exposes the send-to-close loop over a REST API and an MCP server (npx -y @emailpal/mcp): workspaces, domains, mailboxes, warming, campaigns, leads, replies and the pipeline. How to get a key, how one key works across your workspaces, what is covered, what is not, the webhook events, and how read-only mode works.
What you can automate
The REST API and the MCP server cover the whole loop from buying a domain to closing a deal:
- Workspaces: list them, add one to your bill and rename one
- Domains and DNS
- Mailboxes, including pre-warmed inventory
- Warming
- The inbox: read, send, mark and delete
- List verification and suppressions
- Campaigns: settings, schedules, sequences (steps and A/B variants), launch, pause, resume and analytics
- Leads: campaign leads, the lead list, lead statuses and notes
- Replies: list and label them
- The CRM: pipelines, opportunities and tasks
- Webhooks and jobs
- Notifications: your bell and your notification email settings
Base URL: https://www.emailpal.io/api/public/v1. There are 113 endpoints today. The human reference is the API documentation, and the machine-readable one is GET /api/public/v1/openapi.json.
The MCP server wraps those operations as 104 tools for Claude, Cursor, Claude Code, or any MCP client. Each tool is one call to the REST API, so it uses the same keys, scopes and rate limits. One command:
npx -y @emailpal/mcp
with EMAILPAL_API_KEY set. Tool names are prefixed with emailpal_, for example emailpal_create_campaign.
What the MCP tools cover
| Area | Tools | Examples |
|---|---|---|
| Account and balance | 2 | get_account, top_up_balance |
| Workspaces | 3 | list_workspaces, create_workspace, rename_workspace |
| Domains | 10 | search_domains, purchase_domains, verify_dns, release_domain |
| Pre-warmed inboxes | 2 | browse_prewarmed, lease_prewarmed |
| Mailboxes | 6 | list_mailboxes, create_mailboxes, export_mailboxes |
| Warming | 2 | get_warming, set_warming |
| Inbox | 6 | list_inbox, get_message, send_email |
| Verification and suppressions | 4 | verify_list, suppress |
| Jobs | 3 | list_jobs, get_job, retry_job |
| Notifications | 4 | list_notifications, mark_notifications_read, update_notification_preferences |
| Campaigns | 11 | create_campaign, campaign_preflight, launch_campaign, pause_campaign, get_campaign_analytics |
| Campaign leads | 3 | add_campaign_leads, list_campaign_leads, manage_campaign_leads |
| Sequences | 8 | add_campaign_step, update_campaign_step, add_step_variant |
| Leads and statuses | 8 | list_leads, create_lead, set_lead_status, add_lead_note |
| Pipeline | 5 | list_pipelines, create_opportunity, move_opportunity |
| Tasks | 3 | list_tasks, create_task, update_task |
| Replies | 2 | list_replies, label_reply |
Working across workspaces
If you run several workspaces on one bill, for example one per client, a key created on the paying workspace can work in all of them. Name the workspace on each request:
- REST: an
X-Workspace-Idheader with the workspace's id, or aworkspace_idquery parameter. - MCP: every tool takes an optional
workspace_id. Leave it out to use the key's own workspace.EMAILPAL_WORKSPACE_IDsets a default for the whole server, and on the hosted server anX-Workspace-Idheader on the connection does the same.
To read replies from every workspace at once, send X-Workspace-Id: all to GET /inbox or GET /replies, or pass workspace_id: "all" to list_inbox or list_replies. The list covers every workspace the key can reach, newest first, and each item has a workspace_id so you know where to act on it. GET /replies leaves out workspaces that do not have campaigns switched on. An endpoint that does not support all answers it with a 400.
Find the ids with GET /workspaces or list_workspaces. Each workspace comes back with its domain and mailbox counts and how many Unibox threads have unread mail. An admin key on the paying workspace can add a workspace with POST /workspaces (create_workspace) and start setting it up straight away by using the new id.
A key created inside a workspace that someone else pays for reaches only that workspace. Naming any other workspace returns 404 workspace_not_found. Scopes and the hourly request budget belong to the key and do not change between workspaces. See Create an API key for the details.
Notifications over the API
GET /notifications lists the bell of the person who created the key, or who approved the MCP connection, and POST /notifications/read marks notices read. Both accept X-Workspace-Id: all. GET and PUT /notification-preferences read and change that person's notification email settings: per topic in the bell and by email, muted workspaces, the daily digest and a pause. A key that was not created by a person answers 409 no_key_user. Reply notifications and email alerts
Launching a campaign from code
A typical run, whether you script it or an assistant does it:
- Create a draft with
create_campaign. Nothing sends yet. - Add leads with
add_campaign_leads, from a lead list or by value. - Run
campaign_preflight. It is the same Ready to launch checklist as the dashboard, andcan_launchis exactly what launch enforces. See The campaign launch checklist. - Call
launch_campaign. This starts sending real email and sent mail cannot be recalled. - Read replies with
list_repliesand label them withlabel_reply. Labelling sets the lead's status, can open an opportunity and applies blocklist rules, just as it does in the Unibox.
Launching and resuming a campaign need a key with the write scope. They run the same pre-flight as the dashboard and fail with launch_blocked, listing the blockers, if something is wrong.
What is not in the API
The API does not cover everything the dashboard does. Today it has no endpoints for:
- Deleting leads, opportunities or tasks
- Managing lead lists or custom lead statuses. Both are read-only.
- Searching Prospects or building intent audiences, except on the Scale plan, which includes the Prospects and intent leads API. The Prospects and intent leads API. On every plan,
POST /lead-records/{id}/reportreports a single lead-table record and hides it for your account - Bulk actions on the lead list. Pausing, resuming or removing leads works inside one campaign.
- A separate schedule endpoint. Change a campaign's schedules with
PATCH /campaigns/{id}.
The tasks list returns one page and does not take a cursor. The API documentation is the source of truth if this list ages.
How to get an API key
- Sign in → API keys.
- Create a key. Copy it once; we store a hash, not the secret.
- Send it as a bearer token on REST, or as
EMAILPAL_API_KEYfor MCP.
Step-by-step: Create an API key. Client paste configs: Connect the MCP server.
Webhooks
Register an endpoint under Webhooks or with POST /webhooks. Deliveries are signed with an EmailPal-Signature header and are at-least-once, so dedupe on the event id. There are 18 events:
| Event | When it fires |
|---|---|
job.succeeded, job.failed | An asynchronous job finished or failed |
domain.ready, domain.failed | A domain finished setup or failed |
mailbox.provisioned, mailbox.campaign_ready, mailbox.failed | A mailbox was created, finished its 14 days of warming (informational, not a gate), or failed |
list.progress, list.completed | More addresses in a list check were decided (at most one a minute per list), or the check finished |
message.received | Any non-warming message arrived in a mailbox |
message.delivery_updated | The receiving server decided what happened to a send |
reply.received | A prospect answered a campaign email. It says which campaign, step and variant, whether it was an auto-reply, and whether it stopped the sequence |
lead.status_changed | A lead's status changed, from the Unibox, the CRM, the API, a campaign reply or bounce, or an automation |
campaign.paused | A campaign stopped sending, with the reason. auto is true when the platform paused it |
account.restriction_changed | The account's sending was throttled, suspended or restored, or the domains a throttle covers changed. It names the domains and the reason |
domain.restriction_changed | A domain was held, restarted slowly, limited or released. It names the mailboxes, the reason and when a resting domain restarts |
mailbox.restriction_changed | One mailbox's sending hold began or ended, with what caused it |
domain.moved | A domain and its mailboxes moved to another workspace on the same plan. Both workspaces receive it. See Move a domain to another workspace |
reply.received is the event to use for a CRM sync or a notifier. campaign.paused with auto: true is the one worth alerting on. See Auto-pause on bounces.
The three *.restriction_changed events tell your own systems when sending is limited and why, the same text the dashboard shows. Each carries restricted (true when it began, false when it ended), a stable reason code and a plain-language message. A domain hold sends one domain event and one mailbox event per mailbox. The same state is readable by polling: enforcement, enforcement_scope, enforcement_reason, enforcement_since and restricted_domains on GET /account, a restriction block on GET /domains and GET /domains/{id}, and source and reason inside sending_pause on GET /mailboxes/{id}. An endpoint with no event filter receives these events automatically.
Read-only MCP
Set EMAILPAL_MCP_READ_ONLY=true and every mutating tool disappears from the list. Use this when an agent should inspect warming, DNS, campaign results or replies but must not buy domains, send mail, or launch or edit a campaign.
Even without read-only mode, the tools that put mail on the wire or cannot be undone, such as launch_campaign, resume_campaign, send_email, purchase_domains and the delete tools, are marked destructive so a client can ask before it runs them.
Rate limits
Each plan has an hourly API request budget (see the account payload and the docs). Jobs (provisioning, claims, DNS) are asynchronous: create, then poll /jobs.
FAQs
Can I search Prospects or build intent audiences over the API or MCP?
On the Scale plan, yes: it includes the Prospects and intent leads API, with fair-use limits. On Starter and Growth, intent audiences are built in the dashboard, included on every paid plan as unlimited intent leads, and the API can create leads and add them to campaigns (create_lead, add_campaign_leads) and report a lead-table record. Export a list as a CSV from the dashboard if you need the people elsewhere. The Prospects and intent leads API
Is MCP a different product?
No. Same auth, same data, same prices. MCP is how an assistant calls the API without you writing glue.
Do I need a paid plan or an add-on for campaigns and the CRM over the API?
No. The sequencer, Unibox and CRM are part of every plan, and so are the API endpoints for them. A key follows the same rules as the dashboard: sending limits come from each mailbox's own cold-email limit (never above 100 a day) and your campaign's daily limit, not from your plan. See Campaign sending limits and mailbox rotation.
Why do campaign or CRM calls return 404 not_enabled?
Campaigns, leads and the CRM can be switched off for a workspace, and the API answers the same way the pages do. If you expect them to be on, open a ticket with the request id.
Can I use this with Smartlead or Instantly?
REST and SMTP are how those tools send, and you can still use them with EmailPal mailboxes. MCP is for agents operating EmailPal itself: provisioning, warming, verifying, running campaigns and working replies. It does not drive another vendor's campaign UI.
Where is OpenAPI?
GET /api/public/v1/openapi.json and the docs site.
Need More Help?
Create an API key or open a ticket with the request id if a call returns 401/429.