The Lemlist MCP connector routes your AI agent's tool calls to Lemlist's own MCP server through Scalekit. Each user signs in to Lemlist once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 185 tools.
- Tools
- 185
- What they doRead · write · destructive
- 78 · 72 · 3578 read72 write35 destructive
- Users sign in with
- OAuth
- OAuth app
- Scalekit's or your own
Setup
Install the SDK
Terminal window npm install @scalekit-sdk/node dotenvTerminal window pip install scalekit-sdk-python python-dotenvSet your credentials
Add your Scalekit credentials to your
.envfile. Find values in app.scalekit.com > Developers > API Credentials..env SCALEKIT_ENVIRONMENT_URL=<your-environment-url>SCALEKIT_CLIENT_ID=<your-client-id>SCALEKIT_CLIENT_SECRET=<your-client-secret>Create the Lemlist MCP connection
In AgentKit > Connections, create a Lemlist MCP connection. The name you give it is the
connection_nameyour code passes. See Configure connections.Scalekit credentials are available for Lemlist MCP server, so you don't need to register an OAuth app.
Authorize a user and make your first call
quickstart.mts import { ScalekitClient } from '@scalekit-sdk/node'import 'dotenv/config'import { createInterface } from 'node:readline/promises'const scalekit = new ScalekitClient(process.env.SCALEKIT_ENVIRONMENT_URL,process.env.SCALEKIT_CLIENT_ID,process.env.SCALEKIT_CLIENT_SECRET,)const actions = scalekit.actionsconst connector = 'lemlistmcp'const identifier = 'user_123'// Generate an authorization link for the userconst { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })console.log('Authorize Lemlist MCP:', link)const rl = createInterface({ input: process.stdin, output: process.stdout })await rl.question('Press Enter after authorizing...')rl.close()// Make your first callconst result = await actions.executeTool({connector,identifier,toolName: 'lemlistmcp_bulk_get_enrichment_results',toolInput: {},})console.log(result)Terminal window npx tsx quickstart.mtsquickstart.py import osfrom scalekit import ScalekitClientfrom dotenv import load_dotenvload_dotenv()scalekit_client = ScalekitClient(env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),client_id=os.getenv("SCALEKIT_CLIENT_ID"),client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),)actions = scalekit_client.actionsconnection_name = "lemlistmcp"identifier = "user_123"# Generate an authorization link for the userlink_response = actions.get_authorization_link(connection_name=connection_name,identifier=identifier,)print("Authorize Lemlist MCP:", link_response.link)input("Press Enter after authorizing...")# Make your first callresult = actions.execute_tool(tool_input={},tool_name="lemlistmcp_bulk_get_enrichment_results",connection_name=connection_name,identifier=identifier,)print(result)Terminal window python quickstart.pyEach user signs in once. See Authorize a user for the full flow and statuses.
Tools
Pass the exact name toexecute_toollemlistmcp_bulk_get_enrichment_resultsPoll the results of one or more enrichment jobs.Read-onlyBulk Get Enrichment Results
Poll the results of one or more enrichment jobs. Provide a dataRef from bulk_enrich_data or a nextPollRef from a previous poll.
Inputs
dataRefstring- dataRef (tdr_xxx) from a submit tool (first poll) OR nextPollRef from a previous poll result (subsequent polls). Pass it verbatim — do not invent one. Mutually exclusive with enrichmentIds.
enrichmentIdsarray- Array of enrichment IDs to poll (1-500). Mutually exclusive with dataRef.
lemlistmcp_calculate_infrastructureCompute cold-email infrastructure sizing from campaign inputs.Read-onlyCalculate Infrastructure
Compute cold-email infrastructure sizing from campaign inputs. Returns: - Peak daily volume + injection rate - Mailbox count (raw and with safety buffer) - ESP split (Google / Microsoft / SMTP) - Domains needed per seat (cap: 3 mailboxes per domain) - Warmup timeline (rampup + 14-day stabilization) - Lemlist slot cost (included + extras at $9/month) - Brand-consistent domain name suggestions - Mailbox naming proposals per SDR (when sdrs provided), with SDR userId echoed back in `sdrPlans[].userId` if it was passed in All math is deterministic — pure domain function, no LLM math. Use in Step 2 of the outreach-infra skill, after gathering campaign inputs via ask_question.
Inputs
companyDomainstringrequired- Team company domain
delayDaysintegerrequired- Days between sequence steps
espChoicestringrequired- ESP matching strategyone of
googlemicrosoftgoogle+microsoftall-three leadsintegerrequired- Total leads over the timeline
rampupIncrementintegerrequired- Lemwarm daily rampup incrementone of
12 seatsintegerrequired- SDR seats
sequenceStepsintegerrequired- Steps in the sequence
timelineDaysintegerrequired- Campaign duration in days
domainSuggestionsCountinteger- Domain suggestions to return (default 10)
sdrsarray- Optional SDR identities for mailbox naming. Each entry may include the SDR lemlist userId — when provided, it is echoed back in `sdrPlans[].userId` so the caller can wire `assignToUser` in provision_mailboxes without name matching.
lemlistmcp_check_domain_healthCheck DNS health for email sending domains (MX, SPF, DMARC, blacklists).Read-onlyCheck Domain Health
Check DNS health for email sending domains (MX, SPF, DMARC, blacklists). Returns score (0-100), per-check status, and DNS fix records.
Inputs
domainstring- Domain to check (e.g. example.com). If omitted, checks domains from team members.
mailboxIdstring- Mailbox ID to check DNS for.
mailboxIdsarray- Multiple mailbox IDs (usm_xxx) to check in one call (1-20). Returns a per-mailbox array in the same order with an "error" field on failures.
scopestring- Check scope: user (current user) or team (all members). Default: team.one of
userteamdefaultteam
lemlistmcp_display_leadsShow leads in an interactive workspace table.Read-onlyDisplay Leads
Show leads in an interactive workspace table. Users can select, filter, and push to campaigns. For the two-step pattern (lemleads_search in companies mode with `description` -> display_leads), you MUST pass the returned `dataRef` to this tool. The server resolves it to the full company set server-side and injects a `currentCompanyByIds` filter so the leads table queries leads at those companies. This persists in the workspace item (no 30-min TTL concern). Do NOT copy the `companyIds` array into a `currentCompanyByIds` filter inline — emitting hundreds of IDs inline risks truncation; pass them via the `companyIds` field instead and the server builds the filter for you. FOLLOW-UP TURNS over an existing companies table ("find CEOs of those companies", "now show me leads from this list", "and the marketing directors"): the workspace already holds the company set, so DO NOT re-run lemleads_search. Recovery order: 1. If the companies-mode lemleads_search `dataRef` from earlier is still in your context AND was issued in the last ~30 minutes, pass it as `dataRef`. 2. Otherwise, call `get_workspace_items({ itemType: "companies" })`, read `data.companyIds` from the workspace_companies item, and pass that array via the `companyIds` field on this tool. The server builds `currentCompanyByIds` for you (no truncation risk). Re-running lemleads_search to "re-verify the count" loses the curated set and is what produces the bad / off-topic lead lists. Otherwise (no prior companies table involved), call display_leads directly with filters only — the table loads data dynamically. Pass any combination of `filters`, `dataRef`, and `companyIds` together; they are AND-combined server-side. To modify an existing LEADS search: get_workspace_items -> display_leads with updated filters + replaceItemId. Only pass replaceItemId when the target item is itself a leads table — never to convert a companies/messages/other item into leads. To show a different artifact type alongside, omit replaceItemId so a new item is appended.
Inputs
titlestringrequired- Title for the table tab (e.g. "CFOs in France", "SaaS Founders")
columnsarray- Optional column keys to display as a string[] (e.g. ["name","title","company"]). Pass the key strings, not column-descriptor objects. Defaults to name, title, company, etc.
companyIdsarray- Company IDs to scope the leads to. The server builds a currentCompanyByIds filter from these. Accepts both People-Database company IDs (numeric — the recovery path when a prior companies dataRef is expired but the workspace_companies item still holds them: get_workspace_items -> data.companyIds) and lemlist CRM company IDs (cpn_… from search_companies), which the server resolves to People-Database IDs via coreSignalId/domain. When some CRM IDs cannot be resolved, the result reports how many; report that to the user and do NOT claim success on a resultCount of 0. Avoids LLM-side truncation that you would get by inlining a long currentCompanyByIds filter.
dataRefstring- Reference to a previous lemleads_search result. For a people-mode search, the server REPLAYS the exact verified filter set, so passing this dataRef guarantees the displayed table matches the totalCount you just verified (no filter drift, no retyping). For a companies-mode (vector) search, the server injects a currentCompanyByIds filter from the resolved company set. Preferred over retyping filters or inlining IDs (avoids LLM truncation and search/display drift).
filtersarray- People Database filters (PREFERRED). Use filters from get_lemleads_filters. Each needs filterId, in, out arrays.
fromSearchIdstring- searchId (lsh_xxx) returned by an earlier people-mode search. The server replays its stored filters and persists them in the workspace item (no TTL); a filter passed in filters replaces the stored one with the same filterId.
leadIdsarray- Specific lead IDs to display. Only use for small known lists (<50). For results from lemleads_search, pass dataRef instead — it avoids truncating large arrays in the tool call arguments.
replaceItemIdstring- ID of an existing workspace leads item to replace (use for modifications instead of creating a new item)
lemlistmcp_display_leads_pageFetch one page of People Database leads for the workspace-leads table.Read-onlyDisplay Leads Page
Fetch one page of People Database leads for the workspace-leads table. App-only: called by the leads grid iframe (pagination, select-all), not by the agent. Returns the raw Elastic hits the iframe transforms into rows, the total count, and the already-in-campaign / already-in-contacts maps.
Inputs
filtersarrayrequired- Resolved People Database filters (filterId/in/out) for the search
pagenumberrequired- 0-based page index (the usage:"ui" API contract)default
0 sizenumberrequired- Results per page (default 25, max 100)default
25
lemlistmcp_display_tableDisplay a data table in the workspace panel.Read-onlyDisplay Table
Display a data table in the workspace panel. CRITICAL RULES — follow exactly or the table will be empty: 1. dataRef: If the source tool returned a dataRef string, pass it as dataRef (preferred — avoids large data transfer). The rows will be resolved automatically from the stored reference. 2. rows: Only if no dataRef is available. Pass the ORIGINAL data array from the tool result AS-IS. NEVER construct row objects manually or map/transform the array. 3. columns: Use suggestedColumns from the source tool result directly when available. NEVER invent column keys — they must match actual property names in the row objects. PREFERRED: dataRef=result.dataRef, columns=result.suggestedColumns, groupKey=result.groupKey, tableType=result.tableType FALLBACK: rows=result.campaigns, columns=result.suggestedColumns WRONG: rows=result.campaigns.map(c => ({sentCount: c.stats.sentCount})) — this strips data Always forward groupKey and tableType from the source tool when available.
Inputs
columnsarrayrequired- Column definitions — use suggestedColumns from source tool if available
titlestringrequired- Table title (shown in workspace tab)
appendToItemIdstring- Workspace item ID of an existing table to append rows to instead of creating a new tab. Use get_workspace_items to find the ID, then pass it here when loading more data.
dataRefstring- Reference ID from a source tool (e.g. search_campaign_leads). When provided, rows are resolved automatically from the stored data. Preferred over passing rows directly.
groupKeystring- Grouping key for auto-appending rows. Tables with the same groupKey merge into one tab. Use the source tool result value (e.g. campaignId for leads). Required when paginating.
replaceItemIdstring- Workspace item ID of an existing table to overwrite (title, columns and rows) instead of creating a new tab. Required to show new data in a table already on screen: a table with the same title as one already displayed is NOT shown again. Cannot be combined with appendToItemId.
rowsarray- Fallback: the ORIGINAL data array from the source tool result, passed as-is. Only use when dataRef is not available.
tableTypestring- Type of table data. Both values render an "Export as CSV" button for the user: "campaign_leads" for leads coming from search_campaign_leads (server-side export), and "generic" for any other table (the displayed columns and rows are exported client-side). Forward the value from the source tool result when there is one; for a custom table you assembled yourself (e.g. an enriched inbox analysis), set "generic" so the user gets the export button.one of
campaign_leadsgeneric
lemlistmcp_find_watch_list_linkedin_urlsFind real, validated LinkedIn URLs to monitor for a watch list type.Read-onlyFind Watch List Linkedin Urls
Find real, validated LinkedIn URLs to monitor for a watch list type. Returns LinkedIn URLs (person /in/ or company /company/ depending on the type), sourced and validated from live web search and grounded in the team's business context (AI Context Center). Use when the user asks about: - Pre-filling the LinkedIn URLs for a linkedinPeopleProfile, linkedinCompanyProfile, competitorConnections or competitorReactions watch list - Discovering relevant LinkedIn profiles or company pages to watch Important: - Only the 4 LinkedIn signal types are accepted; any other type is rejected. - Pass an optional "focus" to steer the search; otherwise it falls back to the type's name. - Returns an empty list when no relevant URLs are found. - Rate-limited per user per day (a limited number of calls).
Inputs
typestringrequired- Watch list type to find LinkedIn URLs for. Only the 4 LinkedIn signal types are supported: linkedinPeopleProfile (returns /in/ person profiles), linkedinCompanyProfile, competitorConnections, competitorReactions (return /company/ company pages).one of
linkedinPeopleProfilelinkedinCompanyProfilecompetitorConnectionscompetitorReactions focusstring- Optional free-text focus to steer the search (e.g. "HR SaaS in France"). Defaults to the type's feature name when omitted.
lemlistmcp_get_ai_variable_promptsRead the AI-generation prompts of a campaign's AI variable columns (the AI-generated columns in the Leads table: icebreakers, openers, contextual openers, etc.).Read-onlyGet Ai Variable Prompts
Read the AI-generation prompts of a campaign's AI variable columns (the AI-generated columns in the Leads table: icebreakers, openers, contextual openers, etc.). Returns one entry per AI variable: its variable name, the prompt used to generate it, and the AI model. Prompts come back verbatim, with raw {{variable}} placeholders (e.g. {{firstName}}) left unresolved, since a prompt is a template applied across all leads (there is no single lead to resolve against). Use when the user asks to: - See how an AI column (icebreaker, opener, ...) is generated for a campaign - Review or audit the prompt behind a campaign's AI variables before editing it Read-only: no credits consumed, nothing is changed. To edit a prompt, use update_ai_variable_prompt; to add a new column, use create_ai_variable_prompt.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx format) whose AI variable prompts to read
lemlistmcp_get_business_contextReturn the published business context for the current team — markdown describing the company, its value propositions, ICP, products, etc., generated from the team's website.Read-onlyGet Business Context
Return the published business context for the current team — markdown describing the company, its value propositions, ICP, products, etc., generated from the team's website. Use BEFORE: - crafting any messaging, sequence step, or persona suggestion - recommending watch list types (so the AI knows what the team sells and to whom) - onboarding flows that need to reference the team's business Response shape: - data: null → no business context generated yet (suggest running the onboarding generation) - data: { markdown, sourceUrl, generatedAt, lastEditedAt, updatedAt }
Inputs
This tool takes no inputs.
lemlistmcp_get_call_activitiesList call activities from the lemlist dialer (cold calls), with filtering and pagination.Read-onlyGet Call Activities
List call activities from the lemlist dialer (cold calls), with filtering and pagination. Use when the user asks about: - Calls made or received through the lemlist dialer - Filtering calls by campaign, lead, contact, user, status/disposition, direction, or date range - Reviewing call outcomes, durations, or which leads were called Each call includes direction, duration, status/disposition, phone provider, whether a recording exists, the AI transcript summary, and the associated lead/contact/campaign/user IDs. Use get_call_details with an activityId to fetch the recording URL and full transcript for a single call.
Inputs
callStatusstring- Filter by call status/disposition key (e.g. "interested", "no-answer"). The status keys returned by get_call_activities are the valid values.
campaignIdstring- Filter calls by campaign ID (cam_xxx)
contactIdstring- Filter calls by contact ID (ctc_xxx)
dateFromstring- Include calls created on/after this ISO 8601 datetime (e.g. "2026-01-01T00:00:00Z")
dateTostring- Include calls created on/before this ISO 8601 datetime
directionstring- Filter by call direction (inbound or outbound)one of
inboundoutbound leadIdstring- Filter calls by lead ID (lea_xxx)
limitinteger- Max calls per page (default 50, max 100)
pageinteger- Page number (1-based, default 1)
userIdstring- Filter by the lemlist user who made or received the call (usr_xxx)
lemlistmcp_get_call_detailsGet full details for ONE dialer call by its activity ID, including the recording URL and transcript.Read-onlyGet Call Details
Get full details for ONE dialer call by its activity ID, including the recording URL and transcript. Use when the user asks about: - The recording or transcript of a specific call - The full context of a call (lead, contact, company, campaign, call note, disposition) Returns call metadata, the associated lead/contact/company/campaign/user, the manual call note (if any), and — unless includeRecording is false — a temporary recording URL plus the full transcript. Use get_call_activities first to find the activityId.
Inputs
activityIdstringrequired- The call activity ID (act_xxx) — from get_call_activities
includeRecordingboolean- When true (default), also fetch the recording URL and full transcript. Set false to return only call metadata (faster, no recording fetch).
lemlistmcp_get_call_statusesList this team's configured call statuses (dispositions) — the valid KEYS for update_call_status.Read-onlyGet Call Statuses
List this team's configured call statuses (dispositions) — the valid KEYS for update_call_status. Use when: - BEFORE update_call_status, to pick a valid status key for this team (keys are team-specific: defaults plus any custom statuses) - The user asks which call dispositions / statuses exist Returns each status with its key (what update_call_status expects), label, and side effects: whether it marks the lead answered / positive / negative and whether it pauses campaigns.
Inputs
This tool takes no inputs.
lemlistmcp_get_campaign_detailsGet configuration and settings for ONE campaign (timezone, emoji, labels, senders, sequences).Read-onlyGet Campaign Details
Get configuration and settings for ONE campaign (timezone, emoji, labels, senders, sequences). For metrics use get_campaigns_stats, for email content use get_campaign_sequences.
Inputs
campaignIdstringrequired- A single campaign ID (cam_xxx format). MUTUALLY EXCLUSIVE with campaignIds — never send both.
campaignIdsarray- Multiple campaign IDs (cam_xxx) to fetch in one call (1-20). MUTUALLY EXCLUSIVE with campaignId — never send both.
lemlistmcp_get_campaign_sendersRead the sender pool of a campaign, as the campaign editor shows it: each sender with its enabled channels, the selected alias of each mailbox and the diagnostics, plus the sender strategy, the ESP matching and whether the pool is read-only.Read-onlyGet Campaign Senders
Read the sender pool of a campaign, as the campaign editor shows it: each sender with its enabled channels, the selected alias of each mailbox and the diagnostics, plus the sender strategy, the ESP matching and whether the pool is read-only. Keywords: campaign senders, who sends this campaign, from which mailbox, sender pool, sender errors of a campaign. Use when the user asks about: - Who sends a campaign, and from which mailboxes, WhatsApp accounts or phone numbers - Whether a campaign can send: an empty pool, a sender with an error, a required channel without an account - The current pool before a removal: remove_campaign_senders and disable_campaign_sender_channels take the ids from here Returns: campaignId, senderStrategy, espMatching, readOnly (true on a dynamic strategy: the leads fill the pool, no pool write is accepted), senders[] (userId, name, email, channels[] with type, id, label, alias, esp, diagnostics; and the sender diagnostics) and diagnostics (campaign scope, for example no_account_connected for a required channel type). An enabled channel of a type that no step uses carries channel_not_required: it sends nothing. LinkedIn appears only when a step uses it. Only the senders in the pool appear: to see the whole team and who could be added, use get_senders with the campaignId. Load load_skill("senders") to interpret the diagnostics.
Inputs
campaignIdstringrequired- The campaign (cam_xxx), from get_campaigns.
lemlistmcp_get_campaign_sequencesGet the email sequences and their content (subject, body) for a specific campaign.Read-onlyGet Campaign Sequences
Get the email sequences and their content (subject, body) for a specific campaign. Useful for reviewing copywriting and email flow.
Inputs
campaignIdstringrequired- A single campaign ID (cam_xxx format). MUTUALLY EXCLUSIVE with campaignIds — never send both.
campaignIdsarray- Multiple campaign IDs (cam_xxx) to fetch in one call (1-20). MUTUALLY EXCLUSIVE with campaignId — never send both.
lemlistmcp_get_campaignsList campaigns with optional search, filtering by labels, and sorting.Read-onlyGet Campaigns
List campaigns with optional search, filtering by labels, and sorting.
Inputs
createdBystring- Filter by creator user ID (usr_xxx format). Use your own user ID from Context to find campaigns you created.
labelsarray- Filter by labels/tags. OR semantics: returns campaigns matching at least one. Accepts label names ("q2") or IDs ("lab_xxx") — both can be mixed.
limitnumber- Maximum number of campaigns to return (default: 20)default
20 offsetnumber- Number of campaigns to skip for pagination (default: 0). Use with limit to paginate through all campaigns.default
0 searchstring- Search campaigns by name (case-insensitive)
sortOrderstring- Sort by creation date (default: newest first)one of
ascdescdefaultdesc statusstring- Filter by status. OMIT this parameter to get ALL campaigns regardless of status.one of
draftrunningpausedendedarchivederrors
lemlistmcp_get_campaigns_reportsGet lifetime stats for MULTIPLE campaigns in one call.Read-onlyGet Campaigns Reports
Get lifetime stats for MULTIPLE campaigns in one call. Returns metadata, sender info, and 65+ metrics per campaign. No date filtering - for time-based analysis use get_campaigns_stats.
Inputs
campaignIdsarrayrequired- Array of campaign IDs to get reports for (e.g., ["cam_xxx", "cam_yyy"])
lemlistmcp_get_campaigns_statsGet detailed stats for one or more campaigns including lead funnel metrics, message counts, and per-step breakdowns.Read-onlyGet Campaigns Stats
Get detailed stats for one or more campaigns including lead funnel metrics, message counts, and per-step breakdowns.
Inputs
campaignIdsarrayrequired- Array of campaign IDs (cam_xxx format)
ABSelectedstring- Optional: Filter by A/B test variantone of
AB channelsarray- Optional: Filter stats by channels (mirrors the UI dropdown filter). When provided, both top-level totals and messageMetrics.perChannel are restricted to the requested channels. Omit to get the full breakdown across all 5 channels.
endDatestring- End date. Either a calendar day "YYYY-MM-DD" (interpreted as end-of-day in the resolved timezone) or a full ISO 8601 datetime with offset (used as-is). Omit for all-time stats.
sendUserstring- Optional: Filter stats by the sending user. Pass the user id alone ("usr_xxx", preferred); the "usr_xxx|email@example.com" form is also accepted. Get user ids from get_users.
startDatestring- Start date. Either a calendar day "YYYY-MM-DD" (interpreted as start-of-day in the resolved timezone) or a full ISO 8601 datetime with offset like "2026-04-01T00:00:00+05:30" / "2026-04-01T00:00:00Z" (used as-is, the timezone arg is ignored for that bound). Omit for all-time stats.
timezonestring- IANA timezone (e.g. "Asia/Kolkata", "America/New_York") used to interpret startDate/endDate ONLY when they are calendar days (YYYY-MM-DD). Ignored for full ISO 8601 datetimes. If omitted, falls back to the calling user's profile.timezone (if set), then to Europe/Paris. To exactly mirror what the Lemlist UI displays, prefer passing the same ISO 8601 strings the UI uses (capture from the campaignReportsTimePeriod session).
lemlistmcp_get_company_contact_suggestionsRead the contact suggestions of one company: the people its latest buying committee run recommends, each with a People Database leadId, role, fit score and reason.Read-onlyGet Company Contact Suggestions
Read the contact suggestions of one company: the people its latest buying committee run recommends, each with a People Database leadId, role, fit score and reason. Free: it never starts a new run. Use when the user asks to: - Find who to target at a company, before searching the People Database - Fill an ABM campaign with the company's best people Contract rules: - status "not_run": the company has no suggestions yet; search with lemleads_search instead. - status "pending" / "running": suggestions are being computed, contacts stay empty until "completed". - Add the picked people to an ABM campaign with add_abm_campaign_leads, passing their leadId.
Inputs
companyIdstringrequired- The lemlist company (cpn_xxx) whose suggestions to read.
lemlistmcp_get_company_listsGet available CRM company lists.Read-onlyGet Company Lists
Get available CRM company lists. Returns each list's id, name and dynamic flag. Use when the user asks about: - Which company/account lists exist - Finding a clt_xxx list id before add_companies_to_list or import_companies_from_csv Contract rules reproduced by the backend: - dynamic (filter-driven) lists are returned with `dynamic: true`; their membership is computed by their own sync, so add_companies_to_list and remove_companies_from_list reject them - at most 100 lists, newest first, no paging — narrow with search
Inputs
searchstring- Filter lists by name (case-insensitive)
lemlistmcp_get_condition_field_optionsList the exact values a customLeadInfo condition accepts as its `customField`, for one campaign.Read-onlyGet Condition Field Options
List the exact values a customLeadInfo condition accepts as its `customField`, for one campaign. Returns: - contactCustomFields: the team's contact custom fields as { name, display, type } — a condition stores the `name`, bare or as `variables.<name>` - campaignVariableKeys: the extra keys this campaign's own leads carry (empty when no campaignId is given, or when the campaign has no leads yet) - leadVariables: the built-in lead variable keys every campaign carries, written like a custom field - contactFields: the contact fields that have no lead-variable form, each already carrying the `fields.` prefix (e.g. `fields.leadStatus`) — copy the value as listed; any other name goes bare or under `variables.` - companyFields: the team's company fields (defaults, identifiers, company custom fields), each already written as the `company.<field>` a condition stores (e.g. `company.name`) — copy the value as listed Use when: - Before propose_sequence, add_sequence_step, update_sequence_step or edit_condition_branches with a customLeadInfo condition: call it once for the campaign, then pick `customField` from what it returns - The user names a field in their own words and you need the key the campaign actually stores - A write came back rejected as an unknown field Contract rules reproduced by the backend: - customField is a lead variable or a contact custom-field NAME (the stored name, not its display label), written bare or as "variables.<name>" — the campaign editor writes "variables.". Use "fields.<name>" only for the contact fields that have no lead-variable form, listed by get_condition_field_options (leadStatus, industry, location…). A company field is written "company.<field>", listed under companyFields; "companyName" or "variables.companyName" is stored as "company.name". A field that does not exist for the campaign is rejected, unless the campaign has no lead variables yet, in which case it is accepted and checked when the campaign launches. - A contact custom field is addressed by its `name`, never by its `display` label. A label matching exactly one field is rewritten silently; one matching several is rejected. - When you passed a campaignId and campaignVariableKeys came back empty, that campaign has imported no leads yet: any lead-variable name is accepted there and checked again when the campaign launches. With no campaignId the answer is team-level only — it carries no campaign's keys, so it cannot tell you whether a given lead variable exists on one. - Anything else absent from these lists is rejected, with the known keys echoed back.
Inputs
campaignIdstring- Campaign (cam_xxx) whose lead variables to list. Omit for a sequence attached to no campaign yet: campaignVariableKeys then comes back empty.
lemlistmcp_get_contact_fields_schemaGet the list of available fields and relations on a contact (lead) or a company.Read-onlyGet Contact Fields Schema
Get the list of available fields and relations on a contact (lead) or a company. Returns: - standardFields: scalar fields always present on the entity (email, firstName, … / name, domain, …) - customFields: team-specific scalar fields defined by the user - relations: one-to-many relations available as 'include' on search_campaign_leads (contact only) - csvImportTargets: the field keys a CSV column can be mapped to for this entity Use this BEFORE calling search_campaign_leads to know which 'fields' (scalar) and 'include' (relations) values are valid, and BEFORE import_contacts_from_csv / import_companies_from_csv to build columnMapping.
Inputs
entitystringrequired- Entity type to describe: 'contact' (default) or 'company'.one of
contactcompanydefaultcontact
lemlistmcp_get_contact_listsRetrieve available CRM contact lists with optional search filtering.Read-onlyGet Contact Lists
Retrieve available CRM contact lists with optional search filtering.
Inputs
searchstring- Filter lists by name (case-insensitive)
lemlistmcp_get_crm_import_filtersList the filters of the team's connected CRM (HubSpot lists, Salesforce list views or reports, Pipedrive filters, Attio People views) that leads can be imported from into a campaign.Read-onlyGet Crm Import Filters
List the filters of the team's connected CRM (HubSpot lists, Salesforce list views or reports, Pipedrive filters, Attio People views) that leads can be imported from into a campaign. Keywords: import from CRM, HubSpot list, Salesforce list view, Salesforce report, Pipedrive filter, Attio view. Use when the user asks about: - Importing leads from their CRM (HubSpot, Salesforce, Pipedrive, Attio) into a campaign. - Which CRM lists / views / reports are available for a campaign. Contract rules: - The CRM is the one the team is connected to; nothing to pick. - Returns at most 100 filters; `total` counts every match. When total is higher, call again with `search`. - Pass the returned `id` (and the same filterType for Salesforce) to import_crm_leads_to_campaign. - Salesforce only: `ownerScopeUsers` lists the team members who have their own Salesforce account connected. They are the ONLY valid userId values for import_crm_leads_to_campaign; never offer any other team member.
Inputs
filterTypestring- Salesforce only: "lead" or "contact" list views, or "report". Ignored by HubSpot, Pipedrive and Attio. Defaults to "lead" when the Salesforce connection allows Leads, "contact" otherwise.one of
leadcontactreport searchstring- Case-insensitive text matched against the filter name, to narrow a long list.
lemlistmcp_get_domain_dnsRead the current DNS records for a domain (MX, SPF, DMARC, DKIM, CNAME, A…).Read-onlyGet Domain Dns
Read the current DNS records for a domain (MX, SPF, DMARC, DKIM, CNAME, A…). Use in Step 4 of the outreach-infra skill to audit DNS state before writing new records, and to confirm the apex A record is healthy.
Inputs
domainIdstringrequired- Domain ID (dom_xxx)
lemlistmcp_get_inbox_conversationsList inbox conversations with contact info and last message preview, with optional filtering by list type.Read-onlyGet Inbox Conversations
List inbox conversations with contact info and last message preview, with optional filtering by list type.
Inputs
campaignFilterobject- Filter conversations by campaign id(s). Combine with statusFilter to narrow.
dateFilterobject- Filter conversations by last-activity date range (from/to, both inclusive). Combine with the other filters to narrow.
interestFilterobject- Filter conversations by the lead interest set from the heart control (interested / notInterested), across all of the contact's campaigns.
limitnumber- Number of conversations per page (default: 20, max: 50)default
20 listIdstring- Which inbox list to fetch. Options: myConversations (default), unRead, favorites, snoozed, archived, sentOnly, teamConversations, formerMembers (conversations left behind by removed teammates)one of
myConversationsunReadfavoritessnoozedarchivedsentOnlyteamConversationsformerMembersdefaultmyConversations pagenumber- Page number for pagination (default: 1)default
1 searchstring- Search query to filter conversations
statusFilterobject- Filter conversations by the team's contact status / CRM lifecycle (team-defined names, unclassified for contacts with none). For Interested / Not interested, use interestFilter.
tagFilterobject- Filter conversations by custom inbox tag(s). Combine with statusFilter / campaignFilter to narrow.
userIdstring- The user ID to get conversations for (usr_xxx format). If omitted, defaults to the current user. For several members at once, use userIds instead.
userIdsarray- Restrict conversations to these team member user ids (usr_xxx, from get_users), up to 20. Use to fetch the inbox of several specific users at once (e.g. three teammates). When omitted, defaults to the single userId (or the current user).
lemlistmcp_get_inbox_placement_resultRead the result of an inbox placement test started with run_inbox_placement_test.Read-onlyGet Inbox Placement Result
Read the result of an inbox placement test started with run_inbox_placement_test. The test is asynchronous, so call this with the testId until state is "completed". When completed, it returns the per-provider breakdown (Google / Microsoft / SMTP) of where the email landed (inbox / promotions / spam / unreceived percentages), the overall split, and a deterministic `pattern` — trust this field, do not re-derive the thresholds: - "content_borderline": Google/Microsoft inbox but SMTP in spam → fix content, not infra - "deeper_issue": Google/Microsoft also failing → warmup, blacklist, or egregious copy - "all_clean": no content issue, look elsewhere - "mixed": no single dominant pattern; report the per-provider numbers
Inputs
testIdstringrequired- Inbox placement test ID (ipt_xxx) returned by run_inbox_placement_test
lemlistmcp_get_lemleads_filtersGet available filters for People Database searches.Read-onlyGet Lemleads Filters
Get available filters for People Database searches. Call this FIRST before lemleads_search or display_leads/display_companies. Returns filter IDs with valid values.
Inputs
This tool takes no inputs.
lemlistmcp_get_message_templateRead one library message template: name, subject, HTML body, cc, tags, creator and creation date.Read-onlyGet Message Template
Read one library message template: name, subject, HTML body, cc, tags, creator and creation date. Use ONLY when the user explicitly asks about templates: - What a template says, or comparing templates by their body - Applying a template to a step or a reply, at the user's request: after list_message_templates, read the one picked here and copy its exact content into the step as is (subject and body). This fetch is mandatory: the messagePreview of the list is never a substitute for the body Only when the user explicitly asks for a template (by name, tag, author or "use a template"). Never list or apply templates on your own: writing or editing a step from a template is not the default, the default is to write the step yourself. Contract rules reproduced by the backend: - The body is HTML as stored and returned whole, with {{variables}} left raw: keep them as they are when reusing it. Pick the template from list_message_templates (name, subject, messagePreview) before fetching; messageLength there tells how heavy the fetch is. - Not found covers a deleted template, another team's template and a campaign step body alike.
Inputs
templateIdstringrequired- Template to read (etp_xxx), from list_message_templates.
lemlistmcp_get_self_recovery_configGet the current team's self-recovery configuration.Read-onlyGet Self Recovery Config
Get the current team's self-recovery configuration. Use when the user asks about: - Whether self-recovery is enabled - Which signals are monitored or which actions are authorized - How often the recovery summary email is sent Returns the account-level config: enabled flag, monitoredSignals, authorizedActions, digestFrequency.
Inputs
This tool takes no inputs.
lemlistmcp_get_self_recovery_historyList the self-recovery incident history for the team, most recent first.Read-onlyGet Self Recovery History
List the self-recovery incident history for the team, most recent first. Use when the user asks about: - Past self-recovery cases / what was detected and done - Which mailboxes degraded and which actions ran Returns { history, total } — each case carries its recommendations with their execution status.
Inputs
limitinteger- Max cases to return (1-100).
skipinteger- Number of cases to skip, for pagination.
lemlistmcp_get_sender_picture_statusRead whether the lemlist profile picture of a user is synced onto the mailboxes lemlist sold them (Google Workspace, Microsoft 365), in the current team.Read-onlyGet Sender Picture Status
Read whether the lemlist profile picture of a user is synced onto the mailboxes lemlist sold them (Google Workspace, Microsoft 365), in the current team. Use when the user asks about: - Whether their sender picture / avatar shows on their mailboxes - Following up on a sync_sender_picture call Returns { userId, hasPicture, mailboxes: [{ mailboxId (dem_xxx), email, provider, status, syncedAt, lastError, hasOverride }], counts }. - status: synced (the current picture is on the mailbox), not-synced (never pushed, or the picture changed since), queued / syncing (in progress, the cron pushes within ~1 min per tick), failed (lastError says why), not-supported (not a Google or M365 mailbox bought through lemlist: nothing can be done) - hasPicture=false: the user has no lemlist profile picture, so every sync is refused until they upload one in Settings > Account.
Inputs
userIdstring- lemlist userId (usr_xxx) of a teammate. Omit to target the caller.
lemlistmcp_get_sendersList who can send in the team, and with which channels: every team member with the mailboxes (and their aliases), the LinkedIn profile, the WhatsApp accounts and the phone numbers they own, each with its id, its health diagnostics and, with a campaignId, whether the campaign pool enables it (inPool).Read-onlyGet Senders
List who can send in the team, and with which channels: every team member with the mailboxes (and their aliases), the LinkedIn profile, the WhatsApp accounts and the phone numbers they own, each with its id, its health diagnostics and, with a campaignId, whether the campaign pool enables it (inPool). Keywords: available senders, sender candidates, who can send, connected accounts of the team, mailbox id, sender health. Use when the user asks about: - Who in the team can send, or owns a given channel ("who has a connected WhatsApp account?") - Adding or changing senders on a campaign: call it FIRST, with the campaignId, to get the user ids, the channel ids, the aliases and requiredChannelTypes that add_campaign_senders, replace_campaign_senders and enable_campaign_sender_channels need - Why a sender cannot send: a disconnected mailbox, a missing seat, a LinkedIn profile linked on another account Returns: senders[] with userId, name, email, inPool, channels[] (type, id, label, aliases, esp, inPool, diagnostics) and diagnostics (user scope: license_missing, sender_paused). With a campaignId: campaignId, senderStrategy and requiredChannelTypes (the channel types that the steps use; only those can be enabled). On a dynamic strategy the list keeps the senders and the channels that the sender exclusion list in force (campaign override, else team) does not exclude: they are all in the pool. Load load_skill("senders") to interpret the diagnostics and to choose the next tool. To read the exact pool of a campaign (active channels, selected alias, campaign-level diagnostics), use get_campaign_senders.
Inputs
campaignIdstring- Scope the list to a campaign (cam_xxx, from get_campaigns): adds inPool on each sender and each channel, senderStrategy and requiredChannelTypes. Omit it for the team roster without campaign context.
lemlistmcp_get_sequence_step_senderRead the forced sender of one sequence step.Read-onlyGet Sequence Step Sender
Read the forced sender of one sequence step. Use when the user asks: - Who sends a given step, or from which mailbox, WhatsApp account or phone number - Whether a step uses the campaign sender pool or a pinned team member - Before set_sequence_step_sender or clear_sequence_step_sender, to show the current sender Contract rules reproduced by the backend: - The result has no sender when the step uses the campaign sender pool. - sender.channel is absent on a call, manual, API or phone step: those steps pin a user, not an account. - Get the step ids from get_campaign_sequences.
Inputs
sequenceIdstringrequired- Sequence ID (seq_xxx) that contains the step.
stepIdstringrequired- Step ID (stp_xxx) whose forced sender to read.
lemlistmcp_get_settingsRetrieve settings for a campaign or warmup mailbox entity.Read-onlyGet Settings
Retrieve settings for a campaign or warmup mailbox entity.
Inputs
typestringrequired- The type of settings to retrieve.one of
campaignsendinglinkedinLimitsteamlemwarm idstring- Entity ID. Required for "campaign" (cam_xxx) and "lemwarm" (usm_xxx). Ignored for "sending", "linkedinLimits" and "team".
idsarray- For type="lemwarm" only: multiple mailbox IDs (usm_xxx) to fetch in one call (1-20). Provide this OR id.
lemlistmcp_get_snippetRead one snippet: name, HTML content, creator and creation date.Read-onlyGet Snippet
Read one snippet: name, HTML content, creator and creation date. Use ONLY when the user explicitly asks about snippets: - What a snippet contains - Inserting a snippet into a message, at the user's request: after list_snippets, read the one picked here and copy its exact content into the message as is. This fetch is mandatory: the contentPreview of the list is never a substitute for the content Only when the user explicitly asks for a snippet (by name, author or "use a snippet"). Never list or insert snippets on your own: composing a message from a snippet is not the default, the default is to write the message yourself. Contract rules reproduced by the backend: - The content is HTML as authored and returned whole, with {{variables}} left raw: keep them as they are when reusing it. Pick the snippet from list_snippets (name, contentPreview) before fetching; contentLength there tells how heavy the fetch is. - Not found covers a deleted snippet and another team's snippet alike.
Inputs
snippetIdstringrequired- Snippet to read (snt_xxx), from list_snippets.
lemlistmcp_get_statisticsRetrieve statistics for one or more entities of the same type (lemwarm, campaign, lead, etc.).Read-onlyGet Statistics
Retrieve statistics for one or more entities of the same type (lemwarm, campaign, lead, etc.).
Inputs
idsarrayrequired- For "lemwarm": mailbox IDs (usm_xxx) — same ids used with get_settings type="lemwarm". 1-20 items.
typestringrequired- The type of statistics to retrieve. Only "lemwarm" is supported for now.one of
lemwarm daysnumber- Lookback window in days (1-60). Defaults to 30. Applied to every id.
lemlistmcp_get_task_contentLoad the effective message content (subject + body) of a message-based task (opportunity) — the exact content the Focus-mode editor would show and that send_task would send.Read-onlyGet Task Content
Load the effective message content (subject + body) of a message-based task (opportunity) — the exact content the Focus-mode editor would show and that send_task would send. Use when the user asks about: - Previewing what an email / LinkedIn / WhatsApp task will actually send - Checking whether a task already has content before sending or editing it Where the content comes from: - The per-lead draft override if one exists, otherwise the task's template. This is the real outgoing body — NOT the 'content'/'text' fields from get_tasks, which are null for message tasks and must NOT be read as "empty". What it returns: - 'subject'/'message': the effective content ('subject' only for email tasks). - 'hasContent': false ONLY when the body is genuinely empty (the real "no content" case). - 'sendable': true when the content is ready for an immediate send_task (message-based, NON-manual task with a body — and a subject for email). Status, contact and sender are still validated by send_task itself. - 'schedulable': true when the task is a manual campaign step of a message type — it cannot be sent immediately, but send_task will schedule it via the campaign and mark it done ("Schedule & mark done"). A message task is either sendable or schedulable, never both. Read-only: it never edits or sends. Only email / linkedinSend / whatsappMessage tasks carry content; other types return empty content and sendable=false.
Inputs
opportunityIdstringrequired- The task (opportunity) ID to read (opp_xxx format).
lemlistmcp_get_tasksList the team's pending tasks (call/phone, manual, LinkedIn, email tasks...) from the Tasks page.Read-onlyGet Tasks
List the team's pending tasks (call/phone, manual, LinkedIn, email tasks...) from the Tasks page. Use when the user asks about: - Their pending or upcoming tasks ("what tasks do I have", "my call tasks this week") - Counting tasks by priority ("how many high-priority tasks") - Tasks for a specific campaign, assignee, type, or due-date range Filters: - 'type' filters by task type (call/phone, manual, email, linkedin*, sms, whatsappMessage) - 'assignedTo' (usr_xxx) restricts to one user. Omit for the whole team. - 'dueDateFrom'/'dueDateTo' (ISO 8601) bound the due date. - 'campaignId' / 'campaignState' filter by source campaign. - 'priority' (none/low/medium/high) is applied in the handler (the API has no server-side priority filter). The response always includes 'priorityCounts' (distribution over the fetched tasks) so you can answer "how many high-priority tasks". Only pending tasks that are due or upcoming are returned. Tasks are scanned up to a page cap; when 'truncated' is true, counts and totals are partial — narrow with filters for complete results. To show the tasks in a table, call display_table with dataRef=result.dataRef, columns=result.suggestedColumns, tableType=result.tableType. Pass the dataRef — do NOT pass result.tasks as rows: 'tasks' is a truncated preview for your reasoning only, and the full rows are resolved from dataRef (this keeps the table call small and fast). The table surfaces each task's title in a clickable Task column (opens Focus Mode) so the user can reference and open an opportunity. IMPORTANT — 'content'/'text' are NOT the outgoing message body for email / linkedinSend / whatsappMessage tasks: they are usually null here because the real subject/body lives in the task's template (loaded lazily at send time). A null 'content' here does NOT mean the task is empty. To preview what will be sent, call get_task_content; to send, call send_task (it loads the existing content automatically). The 'content'/'text' fields are only meaningful for manual tasks.
Inputs
assignedToarray- Task owners (usr_xxx), any-of. The task's assignee, NOT necessarily whoever clicked Done. Omit for the whole team.
campaignIdarray- Source campaigns (cam_xxx), any-of.
companyIdstring- One CRM company (cpn_xxx): its own tasks and its contacts' tasks.
completedAtFromstring- Only tasks completed (done or ignored) on/after this date (ISO 8601).
completedAtTostring- Only tasks completed (done or ignored) on/before this instant (ISO 8601). A date alone means 00:00 UTC: to include a whole day, pass the next day or a date-time.
contactIdstring- One CRM contact (ctc_xxx). Also covers that contact's campaign leads, so a task stored with only a lead is included.
dueDateFromstring- Only tasks due on/after this date (ISO 8601, e.g. 2026-06-01, 00:00 UTC for a date).
dueDateTostring- Only tasks due on/before this instant (ISO 8601). A date alone means 00:00 UTC: to include a whole day, pass the next day or a date-time.
limitinteger- Rows per page (default 50, max 100).
pageinteger- 1-based page number (default 1).
priorityarray- Priority value(s), any-of: 0 low, 1 medium, 2 high. A task with no priority matches none.
searchstring- Free text over the task title, the contact name and email, and the lead and company names.
sortBystring- Sort field, default "dueDate". Use "handledOn" (completion date) on a done list.one of
dueDatestatuspriorityassignedTocampaignIdscorehandledOncontactJobTitle sortOrderstring- "desc" (default, newest first) or "asc".one of
ascdesc statusarray- Status(es), any-of. Omitted = every status. "pending" = due, upcoming and paused (still to do); "done" = completed; "ignored" = skipped.
typearray- Task type(s), any-of. Calls are stored as "phone" or "call": pass both.
lemlistmcp_get_team_infoGet basic team info (ID, name, plan, credits remaining) and minimal identity of the caller (current user id + email).Read-onlyGet Team Info
Get basic team info (ID, name, plan, credits remaining) and minimal identity of the caller (current user id + email). For full user details call get_users with userIds: ["me"] for the caller, userIds: ["all"] for the full member list, or userIds: ["usr_xxx", ...] for one or more team members.
Inputs
includeMembersboolean- When true, also returns the full list of team members (id, name, email, role) in a single call.
lemlistmcp_get_team_overviewAccount summary: campaign count by status.Read-onlyGet Team Overview
Account summary: campaign count by status. Use get_campaigns for the full list with names and details.
Inputs
This tool takes no inputs.
lemlistmcp_get_unsubscribesList unsubscribed emails with pagination.Read-onlyGet Unsubscribes
List unsubscribed emails with pagination. Use delete_unsubscribe to re-enable.
Inputs
limitnumber- Maximum number of unsubscribes to returndefault
20 offsetnumber- Number of unsubscribes to skip (for pagination)default
0
lemlistmcp_get_user_channelsCheck connected sending channels (email, LinkedIn, WhatsApp).Read-onlyGet User Channels
Check connected sending channels (email, LinkedIn, WhatsApp). Returns connection status, plan availability, and accounts. WhatsApp requires separate addon purchase. Use show_connect_channel to guide setup (one channel at a time).
Inputs
This tool takes no inputs.
lemlistmcp_get_usersRetrieve team member details by user IDs, or pass 'all' to fetch all team members.Read-onlyGet Users
Retrieve team member details by user IDs, or pass 'all' to fetch all team members.
Inputs
userIdsarrayrequired- List of userIds to fetch. Each item is "all" (every team member, lightweight), "me" (caller, full profile), or a usr_xxx id (full profile). 1-20 items.
lemlistmcp_get_watch_list_filter_valueResolve the valid values for one or more watch list filterIds.Read-onlyGet Watch List Filter Value
Resolve the valid values for one or more watch list filterIds. ALWAYS call this tool to get correctly formatted values before passing them to create_watch_list or update_watch_list. Do not guess or hardcode values. filterId MUST come from list_watch_list_filters — call it first to discover which filterIds are valid for the signal type you are configuring. BATCH your lookups: pass every value you need across every filterId in a single call via "requests" (one entry per filterId, with all its queries grouped in "queries"). Do NOT call this tool repeatedly one value at a time. The backend automatically picks the right resolution strategy for each filter. Pass query strings when searching for specific values (e.g. job titles, companies, locations). Each filterId returns the deduplicated union of its queries' matches. An empty results array means no match — do not invent values. The returned matches are candidates, not a selection: a single query can resolve to many values. Do NOT pass all of them back — keep only the ones that actually fit the watch list you are configuring, and drop the rest. If an entry comes back with a non-empty "error" and results null, fix what it describes (e.g. provide a query) and call this tool once more for that filterId.
Inputs
requestsarrayrequired- One entry per filterId, each grouping every value to resolve for it. Batch ALL the lookups you need into this single call — do not call the tool repeatedly one value at a time.
typestring- The signal type being configured (the value passed to list_watch_list_filters). Pass it so a filter whose vocabulary is type-specific (e.g. companyIndustries) resolves against the taxonomy this signal type actually accepts.one of
companyIsHiringcompanyRaisedFundsrecruitmentCampaignnewHirecompanyEmployeeVisitedMyWebsitecustomSignalscompetitorConnectionscompetitorReactionswarmIntrostechnologyChangelinkedinPeopleProfilelinkedinCompanyProfilemergersAcquisitionspromotionlinkedinKeywordsrepublishedJobOffersexternalSignalContactexternalSignalCompanybuyingIntentnewProductLaunchcompanyAwardnewPartnershipnewOfficeclosingOfficecostCuttingoutagesAndSecurityBreacheslawsuitsAndLegalIssuesgithubStargazercompanyRunningAds
lemlistmcp_get_webhooksList all configured webhooks.Read-onlyGet Webhooks
List all configured webhooks. Returns array with _id, targetUrl, createdAt, type, campaignId, isFirst.
Inputs
This tool takes no inputs.
lemlistmcp_lemleads_searchSearch the People Database (450M+ B2B contacts) by people or company.Read-onlyLemleads Search
Search the People Database (450M+ B2B contacts) by people or company. Returns results with total count and pagination.
Inputs
filtersarrayrequired- Array of filter objects from get_lemleads_filters. Each filter MUST have filterId, in (array), and out (array).
modestringrequired- MUST be "people" for contacts or "companies" for organizations. Do NOT use "leads".one of
peoplecompanies descriptionstring- Prose describing the target company as if you were writing ITS "About us" section (companies mode only, max 1000 chars per string). MUST be 2-4 full sentences each, NOT a keyword list. The embedding index was built on real company descriptions, so keyword-stacked strings ("SaaS treasury cash management tool") score poorly. Write complete sentences: who they are, who they serve, what they do, how. GOOD: "We build treasury management software for mid-market finance teams. Our platform helps CFOs and treasurers automate cash forecasting, consolidate bank accounts, and optimize liquidity across entities." BAD: "SaaS software solution for treasury management cash management". Accepts (a) a single string for the simple case, or (b) an array of up to 3 strings covering different angles (e.g. anchor + product + customer/problem). Multi-angle triggers parallel vector searches with RRF fusion — recommended for niche verticals where one phrasing risks missing matches. Combine with keywords (grouped) and country/headcount filters for narrowing.
detailstring- Response verbosity (external MCP clients only; the REST API always returns full rows). "compact" (default) returns the key display/qualification fields per row (~80% smaller); "full" returns every raw field. Use "full" only when you need fields absent from compact.one of
compactfull excludesarray- Fields to exclude from results
fromSearchIdstring- Optional searchId (lsh_xxx) returned by an earlier search of the same mode. The stored filters seed the search; a filter passed in filters replaces the stored one with the same filterId. Not combinable with personaId or description.
keywordsstring- Optional hybrid BM25 + vector terms (companies mode only). Pass as GROUPS whenever possible — each group is a category of signal, and the document must match at least one term per group (AND-of-ORs). Grouping is how you force "editor, not consultancy": one group for the domain, one for the product-type marker. Example for "French SaaS treasury editors": [["treasury management", "cash forecasting", "gestion de trésorerie", "prévisions de trésorerie"], ["software", "SaaS", "logiciel", "platform", "plateforme"]]. A flat string[] is still accepted as a single implicit group (all terms OR-matched, any one hit passes). Keep groups small (2-5 terms each), max 5 groups.
pagenumber- 1-based page index (default: 1)
personaIdstring- Optional saved persona (pdp_xxx) whose filters seed the search. People mode only; rejected on companies. On a conflicting filterId the persona value wins over the one passed in filters. Get an id from list_people_database_personas or create_people_database_persona.
sizenumber- Results per page (default: 10, max: 100)
lemlistmcp_list_campaign_foldersList all campaign folders for the current team, with their hierarchy (parentId).Read-onlyList Campaign Folders
List all campaign folders for the current team, with their hierarchy (parentId). Use when the user asks about: - Seeing their campaign folder structure. - Finding a folder's ID before renaming / moving / deleting it, or before filing campaigns into it. The hierarchy is expressed by `parentId` (absent = root-level folder).
Inputs
This tool takes no inputs.
lemlistmcp_list_domainsList all domains owned by the team (registrar, status, mailbox count).Read-onlyList Domains
List all domains owned by the team (registrar, status, mailbox count). Use to audit the team's existing sending surface before proposing new purchases in the outreach-infra skill.
Inputs
This tool takes no inputs.
lemlistmcp_list_enrichment_providersList the enrichment provider catalog — every provider the team can use in its waterfalls.Read-onlyList Enrichment Providers
List the enrichment provider catalog — every provider the team can use in its waterfalls. Each entry has 'id' (the legal waterfall provider id), 'displayName', 'types' (email and/or phone it can serve) and 'source' ('internal' = lemlist credits, 'external' = the team's own API key / BYOK); external providers also carry 'connected' (whether the team saved an API key). Read this before connect_enrichment_provider to get the connectable external provider ids and their state, or before update_enrichment_waterfalls for the legal provider ids. Team-shared and free (no credits).
Inputs
typestring- Restrict to one enrichment type; omit to return providers for both.one of
emailphone
lemlistmcp_list_enrichment_waterfallsList the team's enrichment waterfalls — the ordered providers lemlist tries when finding an email or phone.Read-onlyList Enrichment Waterfalls
List the team's enrichment waterfalls — the ordered providers lemlist tries when finding an email or phone. Returns the default waterfall of each type (email, phone) plus any custom ones; each provider comes with its execution 'order', 'enabled' flag and 'source' ('internal' = lemlist credits, 'external' = the team's own API key). Read this before update_enrichment_waterfalls to get the current order and the legal provider ids. Team-shared and free (no credits).
Inputs
typestring- Restrict to one enrichment type; omit to return both.one of
emailphone
lemlistmcp_list_mailboxesList the team's mailboxes with their IDs (dem_xxx), email, status, and currently assigned SDR (assignedToUserId).Read-onlyList Mailboxes
List the team's mailboxes with their IDs (dem_xxx), email, status, and currently assigned SDR (assignedToUserId). Optionally filter by domainId. Use to audit which user owns each mailbox — and as the discovery step before `update_mailbox` when the user wants to re-assign mailboxes (e.g. when an outreach infrastructure was provisioned without per-SDR `assignToUser` and every mailbox ended up on the API key owner). Each mailbox also carries a deterministic `ageDays` (no date math needed), and the response includes a `providerMix` summary { google, microsoft, smtp, total, smtpSharePct, smtpHeavy } where `smtpHeavy` is true when SMTP > 70% (structurally disadvantaged). Trust these computed fields.
Inputs
domainIdstring- Optional domain ID (dom_xxx) to scope the listing to one domain
lemlistmcp_list_message_templatesList the team's library message templates: whole reusable messages (a subject and an HTML body) saved from the Templates page or an editor, usable as a step on any channel, an inbox reply or an extension message.Read-onlyList Message Templates
List the team's library message templates: whole reusable messages (a subject and an HTML body) saved from the Templates page or an editor, usable as a step on any channel, an inbox reply or an extension message. Use ONLY when the user explicitly asks about templates: - Which templates the team has, or finding one by name, tag or author - Applying a template to a step or a reply, at the user's request: list here, pick the right one from name, subject and messagePreview, read it with get_message_template, then copy its content into the step Only when the user explicitly asks for a template (by name, tag, author or "use a template"). Never list or apply templates on your own: writing or editing a step from a template is not the default, the default is to write the step yourself. Contract rules reproduced by the backend: - Returns summaries: no body, but messagePreview (the first characters of the body as plain text, variables kept) and messageLength (characters of the HTML body a fetch returns). Pick a template from name, subject and messagePreview, then call get_message_template for the body; messageLength tells how heavy that fetch is. messagePreview is for choosing only: it is plain text, cut, and NEVER the content to write into a step. Always fetch the single template to get the exact HTML body. - search matches the name only, case-insensitive, accents folded on a c e i n o u y. - tags is any-of; userIds is any-of and is a creator filter (usr_xxx from get_users). - Newest first; pages of at most 100. A team can hold hundreds of templates, so do the finding yourself: narrow first with search (a word of the name), tags (read the existing tags with list_message_template_tags) and userIds (resolve the author with get_users), then page with page=2, 3... up to pagination.totalPages when the match is not on the first page or the user wants the whole set. Never stop at page 1 and conclude a template does not exist. - Campaign step bodies are never listed here: read a campaign's steps with get_campaign_sequences.
Inputs
limitinteger- Page size, 1 to 100, default 50.
pageinteger- 1-based page number, default 1.
searchstring- Substring of the NAME only, case-insensitive; accented a c e i n o u y match their plain letter (relance finds Rélance). Bodies and subjects are not searched.
tagsarray- Any of these tags; matched trimmed and lowercased as the library stores them. Read the existing tags with list_message_template_tags.
userIdsarray- Creators, any of (usr_xxx ids from get_users). Omit for every creator.
lemlistmcp_list_people_database_personasList the People Database personas saved by the current team.Read-onlyList People Database Personas
List the People Database personas saved by the current team. A persona is a named, reusable set of People Database filters (an audience definition). Call this FIRST whenever a persona id is needed — a persona id (pdp_xxx) can only be obtained from this tool or from create_people_database_persona. Use when the user asks about: - Which personas / saved audiences the team has - Applying "our ICP" or a persona by name (resolve the name to its id here) - Picking the persona to attach to another resource Each entry returns _id, name, mode, filters, createdAt and updatedAt.
Inputs
modestring- Restrict to personas of one mode. Omit to return every persona of the team.one of
leadscompanies
lemlistmcp_list_saved_searchesList the team's saved People Database searches, sorted by name.Read-onlyList Saved Searches
List the team's saved People Database searches, sorted by name. Optionally filter by mode. Use when the user asks about: - Their saved searches ("my saved searches", "do I have a saved search for X") - Reloading, renaming, updating or deleting a saved search by name (resolve the name to its id here) Each entry returns _id, name, mode, filters, createdAt and lastResultsCount. Re-run one with lemleads_search using its mode and filters.
Inputs
modestring- Only return "people" (leads) or "companies" saved searches. Omit for all.one of
peoplecompanies
lemlistmcp_list_snippetsList the team's snippets: reusable HTML fragments (a signature, a paragraph, a call-to-action) that are inserted INSIDE a message, unlike a message template which is a whole message.Read-onlyList Snippets
List the team's snippets: reusable HTML fragments (a signature, a paragraph, a call-to-action) that are inserted INSIDE a message, unlike a message template which is a whole message. Use ONLY when the user explicitly asks about snippets: - Which snippets the team has, or finding one by name or author - Inserting a snippet into a message, at the user's request: list here, pick the right one from name and contentPreview, read it with get_snippet, then copy its content into the message Only when the user explicitly asks for a snippet (by name, author or "use a snippet"). Never list or insert snippets on your own: composing a message from a snippet is not the default, the default is to write the message yourself. Contract rules reproduced by the backend: - Returns summaries: no content, but contentPreview (the first characters of the content as plain text, variables kept) and contentLength (characters of the HTML content a fetch returns). Pick a snippet from name and contentPreview, then call get_snippet for the content; contentLength tells how heavy that fetch is. contentPreview is for choosing only: it is plain text, cut, and NEVER the content to insert into a message. Always fetch the single snippet to get the exact HTML content. - search matches the name only, case-insensitive, accents folded on a c e i n o u y. - userIds is any-of and is a creator filter (usr_xxx from get_users). - Sorted by name; pages of at most 100. A team can hold many snippets, so do the finding yourself: narrow first with search (a word of the name) and userIds (resolve the author with get_users), then page with page=2, 3... up to pagination.totalPages when the match is not on the first page or the user wants the whole set. Never stop at page 1 and conclude a snippet does not exist.
Inputs
limitinteger- Page size, 1 to 100, default 50.
pageinteger- 1-based page number, default 1.
searchstring- Substring of the NAME only, case-insensitive; accented a c e i n o u y match their plain letter. Contents are not searched.
userIdsarray- Creators, any of (usr_xxx ids from get_users). Omit for every creator.
lemlistmcp_list_voice_profilesList the AI voice profiles available for LinkedIn AI voice note steps (recordMode="ai"): lemlist default voices and the team's own cloned voices.Read-onlyList Voice Profiles
List the AI voice profiles available for LinkedIn AI voice note steps (recordMode="ai"): lemlist default voices and the team's own cloned voices. Use this BEFORE setting a voice on a linkedinVoiceNote step. Pass the returned `voiceId` to add_sequence_step (or in a propose_sequence step) together with recordMode="ai" and the script in `message`. Only ids returned here are accepted; a voiceId from another team is rejected.
Inputs
This tool takes no inputs.
lemlistmcp_list_watch_list_filtersReturn, for each watch list type, the filters is allowed to set.Read-onlyList Watch List Filters
Return, for each watch list type, the filters is allowed to set. Each filter entry includes: - filterId (e.g. "title", "companyIndustries", "location") - name (human-readable label) - properties: which sides (in / out) the form exposes for this filter - required (optional): sides that must be non-empty for the watch list to be valid - requiresQuery (optional): when true, get_watch_list_filter_value MUST be called with a query for this filter; when absent/false, omit the query to get the full set Use BEFORE create_watch_list or update_watch_list to pick valid filterId values and to know which "in"/"out" sides to populate. Omit "type" to get the matrix for all types.
Inputs
typestring- Restrict to a single signal type. Omit to return filters for all types.one of
companyIsHiringcompanyRaisedFundsrecruitmentCampaignnewHirecompanyEmployeeVisitedMyWebsitecustomSignalscompetitorConnectionscompetitorReactionswarmIntrostechnologyChangelinkedinPeopleProfilelinkedinCompanyProfilemergersAcquisitionspromotionlinkedinKeywordsrepublishedJobOffersexternalSignalContactexternalSignalCompanybuyingIntentnewProductLaunchcompanyAwardnewPartnershipnewOfficeclosingOfficecostCuttingoutagesAndSecurityBreacheslawsuitsAndLegalIssuesgithubStargazercompanyRunningAds
lemlistmcp_list_watch_list_libraryList the catalog of watch list signal types available on the platform.Read-onlyList Watch List Library
List the catalog of watch list signal types available on the platform. Use BEFORE create_watch_list to: - know which "type" values are valid (e.g. "companyIsHiring", "jobChange", "linkedinKeywords") - understand what each type monitors (title, description) Each entry includes type, title and description.
Inputs
This tool takes no inputs.
lemlistmcp_list_watch_list_signalsList the signals captured by watch lists for the current team, with filtering and pagination.Read-onlyList Watch List Signals
List the signals captured by watch lists for the current team, with filtering and pagination. Use when the user asks about: - Reviewing newly received signals - Filtering signals by type, status, watch list, or date range - Paginating through historical signals Each signal includes its watch list, type, computed status, received date, and the enriched entity (contact or company).
Inputs
limitinteger- Max signals per page (default 50, max 100)
pageinteger- Page number (1-based, default 1)
receivedAtFromstring- Include signals received on/after this ISO 8601 datetime (with timezone offset)
receivedAtTostring- Include signals received on/before this ISO 8601 datetime (with timezone offset)
sortBystring- Field to sort by (default: receivedAt)one of
receivedAtcreatedAt sortOrderstring- Sort direction (default: desc)one of
ascdesc statusstring- Comma-separated list of computed statuses to filter. Accepted values: new, old, handled, ignored.
typestring- Comma-separated list of signal types to filter (e.g. "companyIsHiring,jobChange"). Accepted values: companyIsHiring, companyRaisedFunds, jobChange, newHire, companyEmployeeVisitedMyWebsite, linkedinProfile, linkedinTopic, customSignals, competitorConnections, technologyChange, linkedinPeopleProfile, linkedinCompanyProfile, mergersAcquisitions, promotion, linkedinKeywords, externalSignalContact, externalSignalCompany.
watchListIdstring- Scope results to a single watch list (wat_xxx format)
lemlistmcp_list_watch_listsList watch lists for the current team with optional type, status, and pagination filters.Read-onlyList Watch Lists
List watch lists for the current team with optional type, status, and pagination filters.
Inputs
limitinteger- Max watch lists per page (default 50, max 100)
pageinteger- Page number (1-based, default 1)
statusstring- Comma-separated list of statuses to filter. Accepted values: active, inactive, draft, insufficient_credits, empty_crm_lists, error. Excludes soft-deleted by default.
typestring- Comma-separated list of watch list types (e.g. "companyIsHiring,jobChange")
lemlistmcp_load_skillLoad specialized guidance for a specific domain (e.g.Read-onlyLoad Skill
Load specialized guidance for a specific domain (e.g. campaign-builder, api-reference) to assist with complex tasks.
Inputs
skillNamestringrequired- Name of the skill to loadone of
campaign-buildercopywritingapi-referencelead-searchcampaign-analysisoutreach-infradeliverability-auditwatch-listsenders sectionstring- Optional: specific reference section to loadone of
sequence-patternsstrategychannels
lemlistmcp_people_database_search_countUse ONLY in the People Database scope.Read-onlyPeople Database Search Count
Use ONLY in the People Database scope. Return the exact number of People Database documents (leads or companies) that match a set of filters, without returning the documents themselves. Use it when: - The user asks how many people or companies match a set of criteria. - To refine a set of filters after people_database_nl_to_filters. - To size an audience before running a full search or building a campaign. - To compare how restrictive two filter sets are. Every filterId must be an exact id returned by people_database_nl_to_filters.
Inputs
filtersarrayrequired- The People Database query — the same "filters" array people_database_nl_to_filters returns. Each filter needs at least one in/out value.
modestringrequired- Search mode: 'leads' for people/contacts, 'companies' for companies. Must match the mode the filters were built for.one of
leadscompanies
lemlistmcp_preview_emailPreview how an email step renders for a SPECIFIC lead.Read-onlyPreview Email
Preview how an email step renders for a SPECIFIC lead. Compiles a sequence step's subject + body for one lead: the lead's {{variables}} are substituted and Liquid conditionals ({% if jobTitle contains "Founder" %}...{% endif %}) are evaluated, exactly like the lemlist UI email preview. Use when the user asks to: - See the final email a given lead will receive - Check how variables / Liquid resolve for a real lead - Spot empty variables or broken Liquid before sending Read-only: no credits consumed, nothing is sent. The lead's campaign is inferred from the lead unless campaignId is passed to override it.
Inputs
leadIdstringrequired- The lead ID (lea_xxx format) whose variables are used to compile the email
stepIdstringrequired- The sequence step ID (stp_xxx format) whose subject + message are compiled
campaignIdstring- Optional campaign ID (cam_xxx format). Defaults to the lead's own campaign; only pass it to override.
lemlistmcp_preview_sequence_updateSAFE READ-ONLY: Preview what would change in an email sequence step before applying modifications.Read-onlyPreview Sequence Update
SAFE READ-ONLY: Preview what would change in an email sequence step before applying modifications. Shows current vs proposed content and campaign status. Must call this before update_sequence_step.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx)
sequenceIdstringrequired- Sequence ID (seq_xxx)
stepIdstringrequired- Step ID (stp_xxx)
newMessagestring- Proposed new message body
newSubjectstring- Proposed new subject line
lemlistmcp_propose_sequencePropose a sequence with full tree structure for user review.Read-onlyPropose Sequence
Propose a sequence with full tree structure for user review. Displays visual tree in workspace. To update an existing sequence: get_workspace_items → propose_sequence with replaceItemId. Format: provide an array of sequences. The main sequence contains the root steps. Conditional steps reference sub-sequences by ID via their "conditions" array. Example with condition: - sequences: [ { _id: "seq_main", steps: [{ _id: "s1", type: "email", delay: 0, name: "Initial email" }, { _id: "s2", type: "conditional", delay: 3, delayType: "within", conditions: [{ sequenceId: "seq_yes", key: "emailsOpened", label: "Opened" }, { sequenceId: "seq_no", fallback: true, label: "Not opened" }] }] }, { _id: "seq_yes", steps: [{ _id: "s3", type: "linkedinSend", delay: 2, name: "LinkedIn follow-up" }] }, { _id: "seq_no", steps: [{ _id: "s4", type: "email", delay: 3, name: "Reminder email", subject: "Following up" }] } ] - mainSequenceId: "seq_main" CRITICAL RULES: - TREE STRUCTURE ONLY: each sub-sequence must be referenced by exactly ONE condition branch. NEVER reuse the same sequenceId in multiple conditions. If two branches need similar steps, duplicate the sub-sequence with different IDs (e.g. "seq_breakup_1", "seq_breakup_2"). - BRANCH CONVERGENCE: to converge multiple branches on a common closing step (e.g. an "Email Breakup"), place that step AFTER the conditional in the PARENT sequence — do NOT duplicate it in every branch. The engine resumes the lead at the parent's next step when a sub-sequence ends. Example: seq_main = [email, conditional(YES->seq_engaged, NO->seq_breakup), email "Final goodbye"] — the final email runs for both branches. - Max 10 levels of nested conditions (a conditional inside a sub-sequence of another conditional, chained up to 10 deep). Nest as deep as the campaign logic genuinely needs within that ceiling; prefer a flatter structure when it expresses the same flow. EDITING AN EXISTING CAMPAIGN (desired-state): when the user asks to review/edit a campaign you already have context for, the proposal must be the FULL intended sequence — describe what the campaign should look like AFTER the edit: - Keep a step: include it with `existing: true` and `sourceStepId` set to its real id (the "id:" shown in get_campaign_context). Do NOT recompose its message — its content is preserved automatically. - Replace a step: same `existing: true` + `sourceStepId`, plus the new content (bind a messageId via compose_messages) and/or a changed `delay`. - Add a step: include it with NO `existing` flag (a brand-new step), at the position you want. - Remove a step: simply OMIT it from the proposal (any live step you don't reference is deleted). On a DRAFT campaign with no launched leads, the full set of edits above is applied. On a RUNNING/PAUSED or lead-populated campaign, only non-destructive edits are accepted: you may replace a step's content and APPEND new steps after the last existing step, but you must keep every existing step (reference each with `existing: true` + `sourceStepId`) and may NOT insert a step between existing ones or remove one — for those, use add_sequence_step (with an explicit index) or delete_sequence_step. Condition types — every conditional step ALWAYS requires `delay`, `delayType` and a non-fallback branch with `key`: - Field-check (hasEmailAddress, hasLinkedinUrl, hasPhoneNumber): check lead data instantly. Use `delayType: "within"` and `delay: 0`. - Action (hasScore, emailsOpened, emailsClicked, emailsUnsubscribed, meetingBooked, linkedinInviteAccepted, linkedinOpened, aircallDone): track lead behavior over time. Use `delayType: "within"` (must occur within `delay` days) or `delayType: "waitUntil"` (wait indefinitely). - Async (linkedinNetworkCheck, hasWhatsappAccount): external API check. Use `delayType: "within"` and `delay: 0`. Step types: email (needs subject), linkedinVisit, linkedinFollow, linkedinInvite, linkedinLikeLastPost, linkedinCommentLastPost (AI-generated comment on the lead's last LinkedIn post; user reviews via Opportunity before publish), linkedinSend, linkedinEndorse, linkedinVoiceNote (LinkedIn voice message. recordMode = how the audio is produced, manual = the "Mark as manual" toggle, independent of each other. recordMode="ai" generates the audio from a composed script via ElevenLabs (also set a voiceId via list_voice_profiles). When no AI voice is available, set `manual: true` so the step becomes a per-lead recording task and the campaign stays launchable - do NOT compose a script or set a voiceId for it), phone, sms, whatsappMessage, manual, conditional, enrichment (⚠️ COSTS CREDITS: automatically enriches leads at this step; provide workflowIds, e.g. ["find_email_01", "find_phone_01"]; requires MULTI plan).
Inputs
mainSequenceIdstringrequired- ID of the root/main sequence
sequencesarrayrequired- All sequences. Main sequence + sub-sequences for conditional branches.
callStatusesarray- Server-derived; do not set manually.
multiBranchBetaboolean- Server-derived; do not set manually.
namestring- Name for this sequence
replaceItemIdstring- ID of an existing sequence item to update (from get_workspace_items). Reuse it to modify a sequence instead of creating a new tab.
skipPlanboolean- Set to true ONLY when the user explicitly asked to skip the campaign plan and go straight to the sequence. It waives the plan-first requirement for a brand-new campaign. Leave unset otherwise — the default flow presents a display_document strategy brief first.
lemlistmcp_recall_memoryRetrieve stored memories from previous conversations to restore context about user preferences or past decisions.Read-onlyRecall Memory
Retrieve stored memories from previous conversations to restore context about user preferences or past decisions.
Inputs
topicstring- Exact topic key to retrieve (e.g. "preferred_tone"). Omit to retrieve all memories.
lemlistmcp_run_deliverability_auditRun the full deliverability audit for the current team and return the complete structured report (header, every phase, verdict).Read-onlyRun Deliverability Audit
Run the full deliverability audit for the current team and return the complete structured report (header, every phase, verdict). This IS the deliverability playbook — the same deterministic engine the lemgod audit page runs, so both surfaces always agree on the same account. Use when the user wants to: - Audit an account's deliverability, or diagnose why open/reply rates dropped or emails land in spam (the deliverability-audit skill) - Get a health check of a sending setup (DNS, warmup, sending volume, content, targeting) The report is READ-ONLY: it never modifies a mailbox, domain, or campaign, and sends nothing. It contains: - header: team, mailbox/domain/campaign counts, account age, lemwarm usage - phases: one result per phase (triage, technical, sending stats, campaign settings, content, warmup), each with a status (ok / minor / broken / skipped) and concrete findings - verdict: root cause, ranked actions, optional recovery plan, and a prose summary Contract rules reproduced by the backend: - All phases always run (no early bail-out); a phase that cannot run returns status "skipped" with a reason — never fabricate data for it. - The verdict's root cause, severity, ranked actions, and recovery plan are computed deterministically; trust them and present them, do not re-derive the thresholds. - Phase 6 (inbox placement) is NOT part of this tool — it sends a real email and is a separate, explicitly-confirmed action (run_inbox_placement_test).
Inputs
This tool takes no inputs.
lemlistmcp_search_campaign_leadsFind leads in your campaigns by email, lead ID, or by listing all leads in a campaign.Read-onlySearch Campaign Leads
Find leads in your campaigns by email, lead ID, or by listing all leads in a campaign.
Inputs
addedAfterstring- ISO 8601 date. Only leads added to the campaign on/after this date (inclusive, UTC). Campaign-listing mode only.
addedBeforestring- ISO 8601 date. Only leads added to the campaign strictly before this date (exclusive, UTC). Campaign-listing mode only.
campaignIdstring- Campaign ID. Required when listing all leads (no email/id), unless campaignIds is provided. Optional filter when searching by email/id.
campaignIdsarray- List leads across several campaigns in one call (campaign-listing mode). Pagination is global across all of them, ordered by add date (newest first).
emailstring- Lead email address to search for
fieldsarray- Custom field names to include on each lead. Discover available names with get_contact_fields_schema.
idstring- Lead ID to search for (alternative to email)
includearray- Optional enrichments to include for each lead. 'notes' = annotated activities, 'activities' = all activities, 'campaigns' = other campaigns the contact is in. When set, max limit is capped at 50.
limitnumber- Max leads to return (default and max: 100).
offsetnumber- Pagination offset (default: 0). Use nextOffset from a previous result to load more.
lemlistmcp_search_companiesSearch your team's Lemlist companies.Read-onlySearch Companies
Search your team's Lemlist companies. Returns a paginated list with each company's id, domain, name, owner, and a curated `crmSync` block describing how the record is synced to your active CRM (Hubspot, Salesforce, or Pipedrive). Use the `crmSyncStatus` filter to find companies in a specific sync state — most notably `unique_index_error_company` to list lemlist companies blocked from syncing because another lemlist company already occupies their CRM record. For each result in that state, look at `crmSync.errors[].metadata.alreadyExistingCompanyId` — that is the canonical lemlist company you should remap contacts to before deleting the duplicate (see `search_contacts` with companyId, then `delete_company`).
Inputs
crmIdsarray- Look companies up by their record id in the team's active CRM (HubSpot, Salesforce, Pipedrive, or Attio), 1 to 100 ids; for Salesforce, the 18-character Account id; for Attio, the record UUID. Returns the companies found, each with the id it matched under crmSync.crmRecordId: an id that matches no company is left out, and none come back when no CRM is connected. Cannot be combined with the other filters.
crmSyncStatusstring- Filter by CRM sync status: synced, not_synced, error, searching (a CRM search is queued or running), not_found (the last CRM search found no record), or one error reason. Requires a CRM (Hubspot, Salesforce, Pipedrive, Attio) to be connected on the team — otherwise the call returns NO_CRM_CONNECTED.one of
syncednot_syncederrorsearchingnot_foundproperty_doesnt_existrequired_field_missingunique_index_error_companycompany_already_exists_with_namecompany_already_exists_with_linkedin_url fieldRejectionReasonstring- Filter to companies carrying a field rejection with this reason (a value lemlist refused to write during CRM sync). Empty list when none match. Independent of crmSyncStatus.one of
crm_sync_duplicate_companycrm_sync_duplicate_company_domaincrm_sync_duplicate_company_namecrm_sync_invalid_domaincrm_sync_linkedin_url_not_companyenrichment_company_duplicate_core_signal_idenrichment_company_duplicate_domainenrichment_company_duplicate_linkedin_urlenrichment_company_duplicate_linkedin_url_sales_navenrichment_company_duplicate_namecrm_sync_invalid_linkedin_urlcrm_sync_invalid_urlcrm_sync_duplicate_linkedin_urlcrm_sync_duplicate_linkedin_url_sales_navcrm_sync_value_oscillatingcrm_sync_owner_sync_loopcrm_sync_unmapped_usercrm_sync_value_incompatiblecrm_sync_unknown_error limitnumber- Max companies to return (1-500, default 100).
listIdstring- Filter to companies that are members of this CRM company list (clt_xxx). Get valid IDs from get_company_lists. A contact list, or a legacy dynamic list read from its saved filters, is rejected. Combines with every other filter.
offsetnumber- Number of companies to skip for pagination (default 0).
searchstring- Free-text search on the company name (case insensitive, accent insensitive).
sortBystring- Sort field (default `createdAt`).one of
createdAt sortOrderstring- Sort direction (default `desc`).one of
ascdesc
lemlistmcp_search_contactsSearch or list your team's Lemlist contacts by name, email, contact list, or attached company.Read-onlySearch Contacts
Search or list your team's Lemlist contacts by name, email, contact list, or attached company. Returns matching contacts with their details (ID, name, email, phone, job title, company, campaign count). All filters are optional — calling the tool without any filter returns the paginated list of all contacts of the team (useful for discovery, e.g. obtaining a Record ID before associating it with another resource). Use this to find if a contact already exists in Lemlist before creating a new one. You can filter by a specific contact list using listId (get valid IDs from get_contact_lists first). Use notInAnyCampaign=true to find contacts that are not part of any campaign (orphan contacts). To list contacts attached to a given company, use companyId (cpn_xxx) or one of companyDomain / companyLinkedinUrl / companySalesnavUrl — only one company* filter at a time. URL/domain filters return an empty list if no company matches. For searching leads WITHIN campaigns, use search_campaign_leads instead. For prospecting NEW leads from the People Database, use lemleads_search instead.
Inputs
companyDomainstring- Filter contacts by their company's website domain (resolved to a companyId). Empty list if no company matches.
companyIdstring- Filter contacts by attached lemlist company ID (cpn_xxx). Mutually exclusive with companyDomain/companyLinkedinUrl/companySalesnavUrl.
companyLinkedinUrlstring- Filter contacts by their company's LinkedIn URL (resolved to a companyId). Empty list if no company matches.
companySalesnavUrlstring- Filter contacts by their company's LinkedIn Sales Navigator URL (resolved to a companyId). Empty list if no company matches.
crmIdsarray- Look contacts up by their record id in the team's active CRM (HubSpot, Salesforce, Pipedrive, or Attio), 1 to 100 ids; for Salesforce, the 18-character Contact or Lead id; for Attio, the record UUID. Each contact found carries the id it matched under crmSync.crmRecordId; statusId and companyId are not returned (left empty). An id that matches no contact is left out, and none come back when no CRM is connected. Cannot be combined with the other filters.
crmSyncStatusstring- Filter by CRM sync state on the team's active CRM: synced (a CRM record and no sync error), not_synced (no CRM record), error (at least one sync error), searching (a CRM search is queued or running), not_found (the last CRM search found no record), or one error reason. Requires a CRM (Hubspot, Salesforce, Pipedrive, Attio) to be connected on the team — otherwise the call returns NO_CRM_CONNECTED.one of
syncednot_syncederrorsearchingnot_foundinvalid_emailproperty_doesnt_existrequired_field_missingunique_index_error_contactcontact_already_exists_with_namecontact_already_exists_with_linkedin_urlcontact_already_exists_with_emailcontact_blocked_by_companycontact_blocked_by_company_searchcontact_blocked_by_company_without_namecontact_blocked_by_company_without_keycontact_blocked_by_company_already_synced emailstring- Exact email address to search for (takes priority over search parameter)
fieldRejectionReasonstring- Filter to contacts carrying a field rejection with this reason (a value lemlist refused to write). enrichment_* = raised while enriching, crm_sync_* = raised during CRM sync. Empty list when none match.one of
crm_sync_duplicate_contact_blockedenrichment_duplicate_emailenrichment_duplicate_linkedin_urlenrichment_duplicate_linkedin_url_sales_navcrm_sync_invalid_phonecrm_sync_invalid_emailcrm_sync_invalid_linkedin_urlcrm_sync_invalid_urlcrm_sync_duplicate_linkedin_urlcrm_sync_duplicate_linkedin_url_sales_navcrm_sync_value_oscillatingcrm_sync_owner_sync_loopcrm_sync_duplicate_company_blockedcrm_sync_unsub_state_protectedcrm_sync_company_data_rejectedcrm_sync_linkedin_url_not_contactcrm_sync_unmapped_usercrm_sync_value_incompatiblecrm_sync_unknown_error limitnumber- Maximum number of contacts to return (1-500, default: 100)
listIdstring- Filter by contact list ID (clt_xxx format). Can be combined with search/email, or used alone to list all contacts in a list. Get valid IDs from get_contact_lists.
notInAnyCampaignboolean- When true, only returns contacts that are not part of any campaign. Can be used alone or combined with other filters.
offsetnumber- Number of contacts to skip for pagination (default: 0)
searchstring- Free-text search: matches against contact full name or email address (min 2 chars, case-insensitive)
withPrimaryCompanyboolean- When true, only returns contacts linked to a company; when false, only contacts without a company. Omit for no filter. Mutually exclusive with the company* filters.
lemlistmcp_search_domainsCheck availability of a domain at the registrar (and optionally return suggestions).Read-onlySearch Domains
Check availability of a domain at the registrar (and optionally return suggestions). Use in Step 3 of the outreach-infra skill — after calculate_infrastructure has produced a shortlist of brand-consistent candidates, call this tool for each candidate to confirm availability + pricing before presenting the plan to the user.
Inputs
basestringrequired- Base domain (e.g. "getacme.com")
includeSuggestionsboolean- Include alternative suggestions from the registrar
lemlistmcp_search_help_centerSearch the lemlist help center for official documentation and guides.Read-onlySearch Help Center
Search the lemlist help center for official documentation and guides. Use this when you need to provide guidance on how to do something in lemlist that you cannot do directly via tools. Returns relevant help center articles with content excerpts and links. Do NOT use this for questions you can answer from context or tools.
Inputs
querystringrequired- Search query in English using 1-3 keywords (e.g., "inbox rotation", "email provider", "signals"). Articles are in English, so translate the user question first.
lemlistmcp_search_linkedin_post_engagersSearch the People Database for the people who commented on or reacted to a LinkedIn post.Read-onlySearch Linkedin Post Engagers
Search the People Database for the people who commented on or reacted to a LinkedIn post. PAID: 50 credits per call, charged once the post is read, even when it yields 0 lead. Use when the user asks about: - The people who engaged with (liked, commented on) a LinkedIn post, theirs or a competitor's, to prospect them - "Who reacted to this post", "the commenters of this post in France", "the engagers of this post who are CTOs" Two-phase confirmation flow: 1. First call WITHOUT confirmed (or confirmed=false): free, checks the link, the plan, the quota, the filters and the credits, and returns the price with status="awaiting_confirmation". Use it to ask the user for explicit approval via ask_question and include that price in the question. 2. Once the user has explicitly approved, call again with the same input plus confirmed=true: the credits are charged and the search runs. Important: - Never call with confirmed=true without explicit user approval of the awaiting_confirmation answer. - A request to retry after a refusal (credits, plan, quota, link, filters) is not an approval: start again at step 1. Contract rules reproduced by the backend: - Paid plan only; the team also needs 50 credits and a People Database search left in its 24h quota. Every refusal happens before the charge. - At most 1000 people, commenters first; isCapped tells when the post had more. - filters are validated before the charge, so a bad filter costs nothing; linkedinPostEngagement and leadLinkedInSlug are refused. - The answer carries a searchId. NEVER call this tool again on the same post to paginate, refine or display: that charges again. Pass the searchId as fromSearchId instead, for free: apply_people_database_filters on the People Database page, display_leads or lemleads_search elsewhere.
Inputs
typestringrequired- Which engagers to collect: "all" (comments first, then reactions), "comments" or "reactions". At most 1000 people are kept, commenters first.one of
allcommentsreactionsdefaultall urlstringrequired- Link of the LinkedIn post: prefer the post page (linkedin.com/posts/...); a lnkd.in short link or a linkedin.com/feed/update/urn:li:activity:... link also works, a feed/update link may fail on a post published in the last hours.
confirmedboolean- Two-phase safety gate. Omit it (or pass false) on the first call to get the preview; pass true only after the user has explicitly approved.
filtersarray- Optional People Database leads filters applied to the engagers in the same search (e.g. a country), from people_database_nl_to_filters or get_lemleads_filters. Validated before any charge; linkedinPostEngagement and leadLinkedInSlug are refused, the search sets them itself.
lemlistmcp_suggest_watch_listsGenerate AI-suggested watch lists for the current team.Read-onlySuggest Watch Lists
Generate AI-suggested watch lists for the current team. Returns scored, ready-to-create watch list configurations — each with a relevance score, a reason and an estimated monthly signal volume — tailored to the team's ICP. Use when the user asks about: - Recommendations on which signals / watch lists to set up - Discovering the most relevant watch list types for their business Important: - Requires the team's business context (AI Context Center) to be set, otherwise it errors. - Consumes one daily AI suggestion session (the team has a limited daily quota). - Returns suggestions only; creating or activating them is a separate step (create_watch_list).
Inputs
limitnumber- Maximum number of agents to return — the highest-scored ones. Keep it small (3-6) unless the user explicitly asks for more. Defaults to a small set when omitted.
reasonstring- Short summary of the user's ICP, sales motion and target geographies (synthesized from the clarifying questions you asked first). Steers which signals score highest. Omit only when the context is truly unknown.
lemlistmcp_test_email_accountTest SMTP/IMAP connectivity of an email account.Read-onlyTest Email Account
Test SMTP/IMAP connectivity of an email account. No actual email sent. Use get_user_channels to find account ID.
Inputs
emailAccountIdstringrequired- The email account ID to test (starts with usm_)
lemlistmcp_validate_campaign_readinessValidate that a campaign is ready to launch by checking step content, sender configuration, DNS health, and daily limits.Read-onlyValidate Campaign Readiness
Validate that a campaign is ready to launch by checking step content, sender configuration, DNS health, and daily limits.
Inputs
campaignIdstring- Campaign ID (cam_xxx format). Provide this or campaignName.
campaignNamestring- Campaign name to search for. Provide this or campaignId.
lemlistmcp_add_campaign_sendersAdd one or more team members to the sender pool of a campaign, or add channels to senders already in the pool.WriteAdd Campaign Senders
Add one or more team members to the sender pool of a campaign, or add channels to senders already in the pool. It never removes a sender or a channel: a sender already in the pool keeps its channels and gains the listed ones. Idempotent. Keywords: add a sender, assign a teammate to a campaign, add Ben to the campaign, send from this mailbox too. Use when the user asks about: - Adding a teammate to a campaign ("add Ben to the Q4 campaign"): one entry with the userId and no channels enables the channel types that the steps require, healthy channels first, exactly as the editor toggle does - Adding a teammate with chosen channels ("add Ben with the mailbox sales@acme.com only"): pass channels with the ids from get_senders - Enabling one channel type on many senders at once (channels on several entries) Before any write: call get_senders with the campaignId. It lists every team member who can send, in the pool or not (inPool), with the user ids, the channel ids, the aliases, the health of each channel and requiredChannelTypes, the channel types that the steps require. Never guess a user id, a mailbox id, a WhatsApp account id or an alias. Load load_skill("senders") first: it tells which tool fits the request, when to change the sender strategy, and how to read the diagnostics. Contract rules reproduced by the backend: - A campaign with a dynamic sender strategy (contactOwner, leadImporter, customField:<field>) has a read-only pool that the leads fill: every pool write is refused. Switch the strategy with set_campaign_sender_strategy first, only when the user wants it. - A channel type is accepted only when a non-forced step of the campaign uses it: no WhatsApp channel without a WhatsApp step. Add the step first with add_sequence_step: it enables the type on each pool sender who owns an account of it, and lists the enabled types in senderPoolChange. - Every entry must be a team member who owns the listed channels; an alias must be one of the aliases of the mailbox. - LinkedIn has no channel to pick: a sender in the pool is enabled on LinkedIn. Never list linkedin with an id. - A pool with more than one sender needs the emailPro plan; the mailboxes above the plan allowance are dropped. - A write runs on a running campaign: the leads not contacted yet get a new sender from the pool, the contacted leads keep theirs. - Every write answers "data": the pool after the change, with the diagnostics of each sender and channel. Read them and report the errors to the user. To define the whole pool at once, use replace_campaign_senders. To add one channel to one sender, enable_campaign_sender_channels does the same with a single entry. No user confirmation required.
Inputs
campaignIdstringrequired- The campaign (cam_xxx), from get_campaigns.
sendersarrayrequired- The senders to add, with user ids and channel ids from get_senders. Put every sender of the request in ONE call: one write, one task sender sync.
lemlistmcp_add_companies_to_listAdd existing CRM companies to a company list.WriteAdd Companies To List
Add existing CRM companies to a company list. Use when the user asks about: - Organizing companies or accounts into lists (e.g. "Target accounts", "Churn risk") - Grouping the companies returned by search_companies Contract rules reproduced by the backend: - company ids come from search_companies; the list id comes from get_company_lists — never invent one - the list must exist, hold companies (not contacts), and not be dynamic - companies already in the list are silently skipped, never duplicated - at most 1000 companies per call
Inputs
companyIdsarrayrequired- Company IDs in cpn_xxx format (max 1000). Get them from search_companies.
listIdstringrequired- Company list ID (clt_xxx format). Get it from get_company_lists.
lemlistmcp_add_contacts_to_listAdd existing CRM contacts to a contact list by list ID.WriteAdd Contacts To List
Add existing CRM contacts to a contact list by list ID.
Inputs
contactIdsarrayrequired- Contact IDs in ctc_xxx format (max 1000). Get them from search_contacts.
listIdstringrequired- Contact list ID (clt_xxx format). Get it from get_contact_lists.
lemlistmcp_add_watch_list_trigger_to_campaignLink an existing watch list to an existing campaign as its signal trigger, making it a signal-based campaign: every new signal the watch list detects pushes its contact into the campaign automatically.WriteAdd Watch List Trigger To Campaign
Link an existing watch list to an existing campaign as its signal trigger, making it a signal-based campaign: every new signal the watch list detects pushes its contact into the campaign automatically. Keywords: signal trigger, signal-based campaign, agent, watch list to campaign, auto-feed a campaign. Use when the user asks about: - Feeding a campaign with a watch list's signals (new hires, job changes, website visitors, hiring companies…) - Adding or setting a trigger / an agent on a campaign - Turning a campaign created through the API or MCP into a signal-based one Contract rules reproduced by the backend: - The campaign and the watch list must belong to the team; a draft is accepted on both sides - A campaign set up as manual in the app is refused: create a signal-based campaign instead, with generate_campaign_for_watch_list or create_campaign_with_sequence followed by this tool - The watch list is switched to signalProcessingType="push_to_campaign"; a campaign with no creation mode yet is stamped signal-based so the trigger shows in its sequence editor - The link uses the default import options: deduplicate on, duplicates handled manually, no backfill of existing signals; they are adjusted in the campaign editor - Additive: a second watch list is added next to the first; detach one with remove_watch_list_trigger_from_campaign - A company watch list without a personaId links but sources no contact, so nothing reaches the campaign and it cannot be launched from the app: set one with update_watch_list first - No user confirmation required
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx) to feed with the watch list signals.
watchListIdstringrequired- Watch list ID (wat_xxx) to link as the campaign signal trigger.
lemlistmcp_associate_crm_list_to_watch_listMonitor the companies or contacts of one or several CRM lists with a watch list.WriteAssociate Crm List To Watch List
Monitor the companies or contacts of one or several CRM lists with a watch list. Use when the user asks about: - Creating a watch list from a CSV file - Monitoring an existing company or contact list instead of the whole market - Changing the CRM lists an existing watch list monitors Flow: 1. CSV only: create_company_list (or create_contact_list), create_upload_url, then import_companies_from_csv (or import_contacts_from_csv) into that list. Map the CSV's domain column (companies) or LinkedIn URL column (contacts): a row without it is not monitored. 2. create_watch_list with activate=false: creates a draft (segmentType="all" is required there, this tool replaces it). 3. This tool, with the draft's watchListId and the CRM list ids. 4. update_watch_list with activate=true (two-phase confirmation) to start monitoring. On an existing watch list, the lists replace what it monitored (entities left out stop being monitored): - active: it monitors the new entities right away - empty (empty_crm_lists: its lists had no entity left): it goes back to active on its own within minutes, no activation needed - paused (or any other non-active status): it stays as is; ask the user whether to reactivate it, then call update_watch_list with activate=true (two-phase confirmation) Contract rules reproduced by the backend: - its signal type must accept CRM lists (some only monitor the whole market) - every list must hold the entity the signal monitors: company lists for a company signal, contact lists for a contact signal - only companies with a domain and contacts with a LinkedIn URL are monitored; when none has one, the call is refused and nothing is written - sending exactly the lists it already monitors is refused: what is added to or removed from those lists is picked up automatically When the association is refused, the tool returns status="error" with code, message, nextStep and details: nothing was written, explain the message to the user and offer what nextStep says.
Inputs
crmListIdsarrayrequired- CRM list ids (clt_xxx) from get_company_lists or get_contact_lists. Each list must hold the entity the signal monitors: company lists for a company signal, contact lists for a contact signal.
watchListIdstringrequired- Watch list ID (wat_xxx): a draft created with create_watch_list and activate=false, or an existing watch list whose monitored lists are replaced
lemlistmcp_bulk_enrich_contactsEnrich existing contacts by ID (ctc_xxx), max 500.WriteBulk Enrich Contacts
Enrich existing contacts by ID (ctc_xxx), max 500. Found data is saved to the contacts and ALL their campaign leads. ASYNC: Copilot receives a dataRef; external MCP clients receive results with enrichmentIds. Poll with bulk_get_enrichment_results. Use when the user wants to enrich existing CRM contacts, including contacts outside campaigns. For campaign lead IDs (lea_xxx), use bulk_enrich_leads. For raw fields without an existing contact ID, use bulk_enrich_data. Duplicate contact IDs are enriched once. Contacts already holding every requested field are skipped without charge. A contact missing any requested field is submitted for the whole requested set. No force option is available; refresh existing data from the lemlist UI. Credit cost, charged only on success — a lookup that returns nothing costs 0: findEmail 5 per email found, linkedinEnrichment 1 per profile enriched, findPhone 20 per phone number found, verifyEmail 1 per email verified. Sum enabled options and multiply by distinct contacts to quote a maximum before asking the user to confirm. Submitting does not mean data was found. Check each summary from bulk_get_enrichment_results before reporting found data.
Inputs
contactIdsarrayrequired- Contact IDs to enrich (ctc_xxx format, 1-500)
findEmailboolean- ⚠️ COSTS CREDITS: Find email from contact data
findPhoneboolean- ⚠️ COSTS CREDITS: Find phone number
linkedinEnrichmentboolean- ⚠️ COSTS CREDITS: Enrich with LinkedIn data
verifyEmailboolean- ⚠️ COSTS CREDITS: Verify each contact email
lemlistmcp_bulk_enrich_dataEnrich up to 500 contacts with additional data in a single call.WriteBulk Enrich Data
Enrich up to 500 contacts with additional data in a single call. Returns a dataRef for polling results asynchronously.
Inputs
itemsarrayrequired- Array of enrichment items (max 500)
lemlistmcp_bulk_enrich_leadsEnrich many existing campaign leads by id in ONE call; the found data is saved onto each lead and its contact and shows in lemlist.WriteBulk Enrich Leads
Enrich many existing campaign leads by id in ONE call; the found data is saved onto each lead and its contact and shows in lemlist. This is the default for enriching leads already in a campaign — use it (not bulk_enrich_data, whose results do not persist) whenever you have lea_xxx ids. ASYNC — lemAgent clients receive a 'dataRef' to pass to bulk_get_enrichment_results; external MCP clients receive an inline 'results' array to poll each enrichmentId. ⚠️ Leads are enriched PER CONTACT, so counts come back in two units. 'leadsTotal' / 'leadsSkipped' are about the ids you submitted; 'contactsTotal' / 'succeeded' / 'failed' / 'skipped' are about the distinct people behind them. Two leads for the same person are ONE lookup and ONE charge — quote costs from the contact count, and read each result's 'leadIds' to report per lead. A contact that already holds EVERY requested field is reported in 'skipped', costs nothing, and its leads are counted in 'leadsSkipped'. A contact missing any one of them is submitted for the whole requested set, so quote its full price. There is no way to force a re-enrichment from this tool: if the user wants to refresh data that already exists, tell them to do it from the lemlist UI. Because the unit is the contact, the found data lands on EVERY lead of that contact in the workspace, including leads in campaigns the user did not name. Credit cost, charged only on success — a lookup that returns nothing costs 0: findEmail 5 per email found, linkedinEnrichment 1 per profile enriched, findPhone 20 per phone number found, verifyEmail 1 per email verified. Sum the enabled options and multiply by the number of DISTINCT CONTACTS (at most the number of leads) to quote a maximum before asking the user to confirm. Submitting an enrichment does NOT mean data will be found. Always confirm the actual result via bulk_get_enrichment_results (check each 'summary') before telling the user any data was found.
Inputs
leadIdsarrayrequired- Campaign lead IDs to enrich (lea_xxx format, 1-500)
findEmailboolean- ⚠️ COSTS CREDITS: Find email from lead data
findPhoneboolean- ⚠️ COSTS CREDITS: Find phone number
linkedinEnrichmentboolean- ⚠️ COSTS CREDITS: Enrich with LinkedIn data
verifyEmailboolean- ⚠️ COSTS CREDITS: Verify each lead email
lemlistmcp_call_apiMake a direct call to the Lemlist API using a specified endpoint and method.WriteCall Api
Make a direct call to the Lemlist API using a specified endpoint and method. Requires load_skill('api-reference') to be called first in the session.
Inputs
endpointstringrequired- API endpoint path starting with /api/
methodstringrequired- HTTP methodone of
GETPOSTPUTPATCHDELETE bodyobject- Request body for POST/PUT/PATCH (optional)
confirmedboolean- Set to true after user confirms a write/delete action
lemlistmcp_clear_sequence_step_senderRemove the forced sender of one sequence step.WriteClear Sequence Step Sender
Remove the forced sender of one sequence step. The step then uses the campaign sender pool again. Use when the user asks to: - Stop sending a step from a specific team member or account - Put a step back on the campaign sender strategy Contract rules reproduced by the backend: - A LinkedIn or WhatsApp voice note step always has a forced sender: use set_sequence_step_sender to change it instead. - The channel type of the step comes back into the pool, as in the campaign editor. senderPoolChange lists the channel types that the change enabled or disabled. It does not tell whether each sender has an account of the type: read get_campaign_senders and report each error diagnostic. - On a running campaign, the pending tasks of the step move to a sender of the pool.
Inputs
sequenceIdstringrequired- Sequence ID (seq_xxx) that contains the step.
stepIdstringrequired- Step ID (stp_xxx) whose forced sender to remove.
lemlistmcp_configure_domain_dnsWrite DNS records for a team-owned domain.WriteConfigure Domain Dns
Write DNS records for a team-owned domain. Two modes: - Provide **records**: replaces the domain's full record set (SPF / DMARC / MX / CNAME etc.). - Provide **dkimRecord**: appends the DKIM TXT record only (after mailbox provisioning). Use in Step 4 (initial SPF/DMARC/MX) and Step 6 (DKIM after Google/M365 gives the public key) of the outreach-infra skill.
Inputs
domainIdstringrequired- Domain ID (dom_xxx)
dkimRecordstring- DKIM record to append
recordsarray- Full DNS record set to write
lemlistmcp_connect_email_accountConnect a custom SMTP/IMAP email account for sending and receiving emails in Lemlist campaigns.WriteConnect Email Account
Connect a custom SMTP/IMAP email account for sending and receiving emails in Lemlist campaigns.
Inputs
imap_hoststringrequired- IMAP server hostname (e.g., "imap.company.com")
imap_loginstringrequired- IMAP authentication login (usually the email address)
imap_passwordstringrequired- IMAP authentication password or app password
imap_portnumberrequired- IMAP server port (typically 993 for TLS)
sender_emailstringrequired- Email address to send from (e.g., "john@company.com")
sender_namestringrequired- Display name for the sender (e.g., "John Doe")
smtp_hoststringrequired- SMTP server hostname (e.g., "smtp.company.com")
smtp_loginstringrequired- SMTP authentication login (usually the email address)
smtp_passwordstringrequired- SMTP authentication password or app password
smtp_portnumberrequired- SMTP server port (typically 587 for TLS or 465 for SSL)
imap_secureboolean- Optional: Use TLS for IMAP connection (default: true)
smtp_secureboolean- Optional: Use TLS for SMTP connection (default: true)
lemlistmcp_connect_enrichment_providerSave the team's own API key for an external enrichment provider (BYOK) so lemlist can call it — Lusha (email + phone) or Apollo (email).WriteConnect Enrichment Provider
Save the team's own API key for an external enrichment provider (BYOK) so lemlist can call it — Lusha (email + phone) or Apollo (email). Once connected the provider becomes available to enable in the enrichment waterfalls (it is added disabled). Lusha keys are stored as-is and NOT validated, so double-check them before saving; Apollo keys are checked with Apollo first and an invalid or unauthorized key returns an error. ZoomInfo has no API key: it connects through its own login in Settings > Integrations, so 'zoominfo' returns an error here. Only external providers can be connected; an internal or unknown id returns an error. Team-shared. WARNING: 'apiKey' is a secret — only pass a key the user explicitly gave you.
Inputs
apiKeystringrequired- The team's own API key for this provider
providerIdstringrequired- External provider id, e.g. 'lusha' (email + phone), 'apollo' (email) or 'zoominfo' (email, OAuth only).
lemlistmcp_copy_campaign_leadsCopy every lead from one campaign into another, in a single call, with FULL fidelity.WriteCopy Campaign Leads
Copy every lead from one campaign into another, in a single call, with FULL fidelity. Prefer this over composing search_campaign_leads + add_leads_to_campaign whenever the user wants to copy / recreate / move / duplicate the leads of a whole campaign into another campaign (e.g. "copy the leads from campaign A into campaign B", "put everyone from the old campaign into the rebuilt one"). Composing the two other tools drops fields (Sales Nav URL, phone, custom variables) and creates new empty contacts — this tool does not. Use when the user asks about: - Copying/recreating a whole campaign's leads into another campaign - Repopulating a freshly duplicated campaign (duplication never copies leads) from the original Contract rules reproduced by the backend: - Works by lead id server-side: it reuses each lead's existing CRM contact (no empty duplicate) and copies every variable (linkedinUrlSalesNav, phone, custom fields). - Both campaigns must belong to the current team; sourceCampaignId and targetCampaignId must be different. - ADDS to the target (source campaign is left untouched). Leads already in the target follow the same dedup rules as a manual push.
Inputs
sourceCampaignIdstringrequired- Campaign (cam_xxx) to copy leads FROM. Every non-deleted lead in it is copied. Get it from get_campaigns.
targetCampaignIdstringrequired- Campaign (cam_xxx) to copy leads INTO. Must be different from sourceCampaignId. Get it from get_campaigns, or create one first.
lemlistmcp_create_ai_variable_promptCreate a NEW AI variable column (an AI-generated column in the Leads table: icebreaker, opener, company research, etc.) on a campaign.WriteCreate Ai Variable Prompt
Create a NEW AI variable column (an AI-generated column in the Leads table: icebreaker, opener, company research, etc.) on a campaign. Provide the column name, the AI-generation prompt, and optionally the model. The column is created team-owned and editable, wired into the campaign so it shows up in the Leads table and generates for leads. Use {{variable}} placeholders in the prompt to reference existing fields or columns. The variable name must be unique on the campaign — creating a duplicate is rejected. To edit an existing column's prompt instead, use update_ai_variable_prompt; to read existing columns, use get_ai_variable_prompts. Creating the column only sets it up — it does NOT generate values. Values are generated per lead later (from the Leads table UI or on import) and that is what consumes credits, not this call. Pass autofill:true to turn the column's auto-generate toggle on, so every lead added or imported to the campaign afterwards (CSV import, CRM sync, manual add) generates it automatically; it stays off by default. To scrape LinkedIn profiles for the column, pass tools:["enrich-linkedin"] AND reference {{linkedinUrl}} in the prompt; enabling any tool forces the premium lemlist model and lemlist-credit billing (per lead, at generation). Use when the user asks to: - Add a new AI column / AI variable to a campaign's leads - Generate a new piece of AI content (research, icebreaker, summary) per lead - Have an AI column filled automatically for every lead imported into a campaign This mutates campaign configuration, so confirm the column name and prompt with the user before calling.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx format) the new AI variable column is added to
promptstringrequired- The AI-generation prompt for the new column. Use {{variable}} placeholders (e.g. {{firstName}}) to reference fields or other existing columns.
variableNamestringrequired- Name of the new AI variable column to create (e.g. "companyNews"). Must be unique on the campaign and a valid identifier (letters, digits, underscores, starting with a letter). The "variables." prefix is optional.
autofillboolean- Sets the column's "auto-generate" toggle. When true, this AI variable is generated for every lead added or imported to the campaign afterwards (CSV import, CRM sync, manual add). Defaults to false, leaving generation to be triggered from the Leads table. ⚠️ COSTS CREDITS: with it on, every newly added lead consumes one generation.
modelstring- Optional AI model id powering the column (e.g. "gpt-5.4", "claude-sonnet-4-6", "sonar-pro"). Omit to use the team default model. Use a web-search model (e.g. Perplexity "sonar-pro") for columns that research live data. Ignored when tools are set (tools force the premium lemlist model).
toolsarray- Optional AI tools the column may use while generating each value. "enrich-linkedin" scrapes the lead's LinkedIn profile (the prompt MUST reference {{linkedinUrl}} or {{linkedinUrlSalesNav}}, otherwise the tool never fires and the column stays empty); "scrape-website" reads a URL; "web-search" searches the live web. ⚠️ COSTS CREDITS: enabling ANY tool forces the premium lemlist model and switches billing to lemlist credits, charged per lead at generation time (not now). Omit for a plain text column.
lemlistmcp_create_campaign_folderCreate one or more campaign folders that share the same parent, in a single call.WriteCreate Campaign Folder
Create one or more campaign folders that share the same parent, in a single call. Use when the user asks about: - Adding one or several folders at the same level (pass them all in `names`). - Creating sub-folders under an existing folder (pass its `parentId`). For a nested chain (A > B > C) call once per level instead, threading each returned folderId as the next `parentId`. Contract rules reproduced by the backend: - Each name must be unique among folders that share the same parent. - Depth is capped at 15 levels, 50 folders per level, 500 folders per team. - Best-effort: valid names are created even if others fail; the response lists `created` and `failed` (with a per-name reason).
Inputs
namesarrayrequired- Names of the folders to create under the SAME parent (1-100). Pass every sibling folder in one call rather than calling repeatedly. Each name must be unique among folders sharing that parent. (To build a nested chain instead, call once per level, threading each returned folderId as the next parentId.)
colorstring- Optional color (hex) applied to every folder created. Omit for no color.one of
#F87171#FB923C#FBBF24#A3E635#34D399#60A5FA#A78BFA#F472B6#9CA3AF parentIdstring- Parent folder ID (cfo_xxx) all the new folders are nested under. Omit to create them at the root.
lemlistmcp_create_company_listCreate a new static company list in the team's CRM.WriteCreate Company List
Create a new static company list in the team's CRM. Use when the user asks about: - Grouping companies or accounts into a new list that does not exist yet - Preparing a destination before add_companies_to_list Contract rules reproduced by the backend: - the list is always static; filter-driven (dynamic) lists are built in the web app, not here - the list starts empty — add its members with add_companies_to_list - check get_company_lists first: a list with that name may already exist
Inputs
namestringrequired- Name of the company list to create
lemlistmcp_create_contact_listCreate a new static contact list in the CRM to organize and group contacts.WriteCreate Contact List
Create a new static contact list in the CRM to organize and group contacts.
Inputs
namestringrequired- Name of the contact list to create
lemlistmcp_create_enrichment_waterfallsCreate one or more custom enrichment waterfalls in ONE call, each serving the leads whose country is in 'conditions.country.in' (ISO 3166-1 alpha-2).WriteCreate Enrichment Waterfalls
Create one or more custom enrichment waterfalls in ONE call, each serving the leads whose country is in 'conditions.country.in' (ISO 3166-1 alpha-2). A country may belong to only one waterfall per type (email, phone); leads with no country or an unassigned country use the default waterfall of the type. 'providers' is the execution order (first tried first); providers left out are appended disabled. Names are unique within a type, and a type holds at most 20 custom waterfalls. Run list_enrichment_waterfalls first to see the countries already taken and the legal provider ids. Entries are applied independently: 'results' reports the created waterfall or an error per name. Team-shared and free (no credits).
Inputs
createsarrayrequired- One entry per waterfall to create; applied independently.
lemlistmcp_create_in_crmREQUIRES CONFIRMATION: Push lemlist contacts or companies to the team's connected CRM — the grid's "Create in CRM".WriteCreate In Crm
REQUIRES CONFIRMATION: Push lemlist contacts or companies to the team's connected CRM — the grid's "Create in CRM". Each record is searched in the CRM first: linked when found, created there otherwise. Use when the user asks about: - Pushing, syncing or creating contacts or companies in their CRM - Forcing contacts that are missing from the CRM into it Two-phase confirmation flow: 1. First call without confirmed: returns status="awaiting_confirmation" and runs nothing. Tell the user which records will be pushed, that the missing ones will be created in the CRM, and ask for approval. 2. Only after the user replied yes, call again with the same arguments and confirmed=true. The user's request is not the approval: never send confirmed=true in the same turn as the first call. Contract rules reproduced by the backend: - entity="contact" takes idsOrEmails (ctc_xxx ids or emails), entity="company" takes idsOrDomains (cpn_xxx ids or domains); or listId (clt_xxx) for a whole list of that entity, never both - At most 10000 records per call, however they are named: references (TOO_MANY_RECORDS otherwise), the members of a listId (LIST_TOO_LARGE otherwise), or the companies the domains match, since a domain matches every company carrying it (TOO_MANY_MATCHES otherwise). References matching no lemlist record come back in notFound - A record already linked to a CRM record is synced with it again instead of searched (alreadyLinked) - A record with no email, domain or LinkedIn URL cannot be searched: it is left untouched and counted in notSearchable - A record whose CRM search is already queued is not queued twice (alreadyInProgress) - Requires a connected CRM (HubSpot, Salesforce, Pipedrive, Attio, Zoho): NO_CRM_CONNECTED otherwise - Processing is ASYNCHRONOUS: the counts say what was queued, not what happened. Never present the action as done, and do not poll. When the user asks for the outcome, read it on the records: search_contacts / search_companies with the same listId and crmSyncStatus (searching = still running, synced, not_found) after a list, or after references the crmSync block search_contacts (by email or name) / search_companies (by name) return on each record. Never repeat crmSyncStatus without listId to wait for the outcome: that filter reads every record of the team - Only a team admin can create records in the CRM (CREATE_IN_CRM_NOT_ALLOWED otherwise) - asLead=true creates Salesforce Leads, for contacts on Salesforce only (AS_LEAD_NOT_SUPPORTED otherwise) - On Salesforce, the connection must grant the object created (Contact, Account or Lead): CRM_OBJECT_NOT_ALLOWED otherwise
Inputs
entitystringrequired- Which lemlist records to act on: contacts or companies.one of
contactcompany asLeadboolean- Salesforce and entity="contact" only: create the contacts not found as Salesforce Leads instead of Contacts.
confirmedboolean- REQUIRED for the action to run: true only after the user explicitly approved it. Omit or false to get the confirmation request first.
idsOrDomainsarray- entity="company" only: lemlist company ids (cpn_xxx) or domains, 10000 at most.
idsOrEmailsarray- entity="contact" only: lemlist contact ids (ctc_xxx) or email addresses, 10000 at most.
listIdstring- Acts on every record of this list (clt_xxx, from get_contact_lists / get_company_lists), instead of idsOrEmails / idsOrDomains. The list must hold the entity and 10000 records at most.
lemlistmcp_create_or_update_companyCreate a new company in the user's Lemlist company database, or update an existing one (upsert).WriteCreate Or Update Company
Create a new company in the user's Lemlist company database, or update an existing one (upsert). Requires both a name AND a domain. If a company with the same domain, LinkedIn URL, or Sales Navigator URL already exists, it will be updated instead of creating a duplicate. Returns the company data and whether it was created or updated.
Inputs
domainstringrequired- Company website domain (e.g. "acme.com"); a name alone only matches a company that has no domain. Upsert matching key, required unless linkedinUrl or linkedinUrlSalesNav is given
namestringrequired- Company name. Required
companyOwnerstring- Company owner email or user ID (usr_xxx format)
descriptionstring- Description
employeeCountstring- Employee count
employeeGrowthstring- Employee growth (3m)
foundedOnstring- Founded on
headquartersstring- Headquarters
industrystring- Industry
linkedinUrlstring- Company LinkedIn page URL (linkedin.com/company/...), the strongest match key; a personal profile URL is rejected
linkedinUrlSalesNavstring- Company Sales Navigator URL (linkedin.com/sales/company/...), match key right after the LinkedIn page URL
locationstring- Location
picturestring- Company logo URL
revenuestring- Revenue
sizestring- Size
specialtiesstring- Specialties
subIndustrystring- Sub-industry
taglinestring- Tagline
technologiesstring- Technologies
typestring- Type
updateStrategystring- How an EXISTING record is updated: overwriteIgnoreEmpty (default, the behavior when absent: incoming values replace stored ones, an empty incoming value never clears), overwrite (same, but an empty string clears the field), fillEmptyOnly (only fills fields the record has empty, stored values and identifiers are kept). A new record always receives every value sent.one of
overwriteoverwriteIgnoreEmptyfillEmptyOnly
lemlistmcp_create_or_update_contactCreate a new contact in the user's Lemlist contact database, or update an existing one (upsert).WriteCreate Or Update Contact
Create a new contact in the user's Lemlist contact database, or update an existing one (upsert). Requires at least an email OR linkedinUrl as identifier. If a contact with the same email or LinkedIn URL already exists, it will be updated instead of creating a duplicate. Returns the contact data and whether it was created or updated. Use search_contacts first to check if the contact already exists.
Inputs
companyDomainstring- Company domain to link the contact to a company (alternative to companyId)
companyIdstring- Company ID to associate the contact with
companyLinkedinUrlstring- Company LinkedIn URL to link the contact to a company (alternative to companyId)
contactIdstring- Existing contact ID (ctc_xxx) to update in place (update-only, never creates). The call fails if the id does not exist.
contactOwnerstring- Contact owner email or user ID (usr_xxx format)
emailstring- Email address. Required if linkedinUrl and contactId are not provided
fieldsobject- Custom fields to set, as a map of customFieldName -> value (e.g. {"primary_contact": true}). Names must match the team custom fields (call get_contact_fields_schema to list them). Values must be a non-empty string, a number or a boolean: false is written as-is, while an empty value is refused because the API cannot clear a custom field.
firstNamestring- First name
industrystring- Industry of the person
jobDescriptionstring- Job description
jobTitlestring- Job title
languagesstring- Languages
lastNamestring- Last name
linkedinUrlstring- LinkedIn profile URL (raw strings like linkedin.com/in/foo accepted). Required if email and contactId are not provided
linkedinUrlSalesNavstring- Sales Navigator profile URL (linkedin.com/sales/lead/...); a public profile URL goes in linkedinUrl
locationstring- Location of the person
peopleDbConnectionCountstring- LinkedIn connections count of the person, from the People Database; 500 usually means 500 or more
peopleDbDepartmentstring- Department of the person, from the People Database
peopleDbSenioritystring- Seniority of the person, from the People Database
phonestring- Phone number
picturestring- Profile picture URL
salesnavUrlstring- Deprecated alias of linkedinUrlSalesNav, still accepted
skillsstring- Skills
summarystring- Summary
taglinestring- Tagline of the person
timezonestring- Timezone
updateStrategystring- How an EXISTING record is updated: overwriteIgnoreEmpty (default, the behavior when absent: incoming values replace stored ones, an empty incoming value never clears), overwrite (same, but an empty string clears the field), fillEmptyOnly (only fills fields the record has empty, stored values and identifiers are kept). A new record always receives every value sent.one of
overwriteoverwriteIgnoreEmptyfillEmptyOnly
lemlistmcp_create_people_database_personaCreate a People Database persona for the current team.WriteCreate People Database Persona
Create a People Database persona for the current team. Use when the user asks about: - Saving a set of People Database filters as a reusable audience - Defining an ICP / persona to reuse later Contract rules reproduced by the backend: - name must be non-empty and unique within the team (duplicates are rejected) - mode must be "leads": company personas are not supported yet - every filterId must be an exact id from the People Database filter catalog (GET /api/database/filters) - filters gated by a plan the team does not have are silently dropped Returns the created persona id (pdp_xxx).
Inputs
filtersarrayrequired- People Database filters defining the persona
modestringrequired- Search mode the filters target. Only "leads" is accepted today.one of
leadscompanies namestringrequired- Persona display name (non-empty, unique per team)
lemlistmcp_create_tasksCreate one OR many standalone tasks (opportunities) on contacts, companies or campaign leads: to-dos listed on the Tasks / Focus page, each assigned to a teammate and due at its own date, for a person to complete.WriteCreate Tasks
Create one OR many standalone tasks (opportunities) on contacts, companies or campaign leads: to-dos listed on the Tasks / Focus page, each assigned to a teammate and due at its own date, for a person to complete. Use when the user asks about: - Creating a follow-up, reminder or to-do task on one or several contacts, companies or leads - Planning a call, or an email / LinkedIn / WhatsApp message to send later from the Tasks page - Assigning a task to a teammate with a due date and a priority Pass every task in ONE call through tasks[] (1 to 100), each with its own record, title, type, content, priority, due date and assignee. The result is always the aggregate { total, succeeded, failed, results[] }: each entry carries the task's index in tasks[], its record ID, and either opportunityId or an error code. Contract rules reproduced by the backend: - Exactly one of contactId, companyId or leadId per task. A lead task is linked to the lead's contact, shows the lead's campaign as its Task source, and follows its own due date and assignee alongside the lead's sequence. - A company task can only be of type "manual". - message holds the instructions of the task; content is the message to send (email, linkedinSend, whatsappMessage only); subject is email only. - assignedTo must be a teammate (list them with get_users); it defaults to the current user. - dueDate defaults to now; a past date creates a task that is already due. - A task that breaks one of the rules above rejects the whole call before any task is created: fix it and send the full list again. - A task whose record or assignee is not found fails on its own (error code in its results[] entry); the other tasks are still created. - A message task holds the message for later: it is sent with send_task, once the user approves. After the call: - Link every created task as [title](task://opp_xxx), with the exact title you sent and its opportunityId. - Retry ONLY the failed indexes: calling again with the whole list creates the successful tasks a second time.
Inputs
tasksarrayrequired- Tasks to create, 1 to 100 per call. Each task carries its own record, title, type, content, priority, due date and assignee.
lemlistmcp_create_upload_urlGet a presigned URL to upload a file from the user's machine to lemlist storage, then reference it by key.WriteCreate Upload Url
Get a presigned URL to upload a file from the user's machine to lemlist storage, then reference it by key. Use this to AVOID retyping large file contents through the conversation. Flow: 1. Call this tool with purpose, fileName, and the EXACT fileSize in bytes. 2. Upload the raw file to the returned uploadUrl with an HTTP PUT, e.g.: curl -T leads.csv -H "Content-Type: <contentType>" "<uploadUrl>" 3. Pass the returned uploadKey to the tool that consumes it: - purpose="leadsCsv" → import_leads_to_campaign - purpose="contactsCsv" → import_contacts_from_csv - purpose="companiesCsv" → import_companies_from_csv - purpose="csvData" → read_uploaded_csv The URL is short-lived (expiresInSeconds). fileSize MUST be the real byte size of the file — it is signed into the URL, so a mismatch makes the upload fail, and a size above the limit is rejected up front.
Inputs
fileNamestringrequired- Original file name (e.g., "leads.csv"). Used to derive the extension and MIME type.
fileSizenumberrequired- EXACT file size in bytes. Signed into the URL — the upload fails on mismatch and the call is rejected above the limit.
purposestringrequired- What the file is for. "leadsCsv" → import_leads_to_campaign, "contactsCsv" → import_contacts_from_csv, "companiesCsv" → import_companies_from_csv, "csvData" → read_uploaded_csv.one of
leadsCsvcontactsCsvcompaniesCsvcsvData contentTypestring- MIME type override. Defaults to the type inferred from the file extension.
lemlistmcp_create_watch_listCreate a new watch list for the current team.WriteCreate Watch List
Create a new watch list for the current team. Use when the user asks about: - Starting to monitor a new signal type - Setting up a watch list end-to-end (create + configure + activate) - Creating a draft watch list that the user will configure later in the UI Contract rules reproduced by the backend: - activate=true ⇒ segmentType AND signalProcessingType must be set - signalProcessingType="create_opportunity" ⇒ signalOpportunityTemplate required (and forbidden otherwise) - personaId is accepted ONLY on a company watch list with signalProcessingType="push_to_campaign" (any other config is rejected), and only for teams in the personas beta - personaId is optional there: without it the signals are still recorded, they simply source no contact, so nothing reaches the campaign - get a personaId from list_people_database_personas (or create_people_database_persona); never invent one - Only segmentType="all" is accepted via API (CRM lists and CSV imports are not)
Inputs
activatebooleanrequired- When true, activates the watch list immediately (advances to VALIDATED step, triggers billing and external watcher setup). Requires segmentType and signalProcessingType to be set.
filtersarrayrequired- Configuration filters as an array of { filterId, in[], out[] } objects
namestringrequired- Watch list display name (non-empty)
segmentTypestringrequired- Segment type to monitor. Only "all" is supported via API. Required together with signalProcessingType when activate=true.
signalProcessingTypestringrequired- How signals are processed once the watch list runs. Required together with segmentType when activate=true. When set to "create_opportunity", signalOpportunityTemplate is required (and forbidden for the other types).one of
manualcreate_opportunitypush_to_campaign typestringrequired- Watch list signal typeone of
companyIsHiringcompanyRaisedFundsrecruitmentCampaignnewHirecompanyEmployeeVisitedMyWebsitecustomSignalscompetitorConnectionscompetitorReactionswarmIntrostechnologyChangelinkedinPeopleProfilelinkedinCompanyProfilemergersAcquisitionspromotionlinkedinKeywordsrepublishedJobOffersexternalSignalContactexternalSignalCompanybuyingIntentnewProductLaunchcompanyAwardnewPartnershipnewOfficeclosingOfficecostCuttingoutagesAndSecurityBreacheslawsuitsAndLegalIssuesgithubStargazercompanyRunningAds campaignIdstring- Campaign (cam_xxx) to link as this watch list's signal trigger: every new signal pushes its contact into that campaign, and a campaign with no creation mode yet is stamped signal-based so the trigger shows in its sequence editor. Only with signalProcessingType = "push_to_campaign"; a campaign set up as manual is refused. Same effect as add_watch_list_trigger_to_campaign, in one call.
confirmedboolean- Set to true only after the user has explicitly confirmed activating this watch list (e.g. via ask_question). Only read when activate=true; never set it on the first call.
emojistring- Optional emoji for the watch list (a random one is picked if omitted)
personaIdstring- Persona (pdp_xxx) used to source contacts from the People Database. Used by a company watch list with signalProcessingType = "push_to_campaign" whose signal type does not already ship its own contact: those company signals carry no contact, so the persona is the only way to know who to reach. Optional — without it the signals are still recorded, they simply source no contact, so nothing reaches the campaign. Rejected on any other configuration, including a signal type that delivers its own contact ("newHire", "promotion"), whose signals reach the campaign on their own.
signalOpportunityTemplateobject- Opportunity template payload, required when signalProcessingType = "create_opportunity"
lemlistmcp_crm_search_and_connectREQUIRES CONFIRMATION: Search the team's connected CRM for lemlist contacts or companies and link each one to the CRM record found — the grid's "Search & connect".WriteCrm Search And Connect
REQUIRES CONFIRMATION: Search the team's connected CRM for lemlist contacts or companies and link each one to the CRM record found — the grid's "Search & connect". Creates no CRM record (use create_in_crm for that), but each record it links then syncs its mapped fields with that CRM record, which can update it. Use when the user asks about: - Linking contacts or companies to their existing CRM records, or relaunching their CRM sync - Finding out which contacts or companies already exist in the CRM Two-phase confirmation flow: 1. First call without confirmed: returns status="awaiting_confirmation" and runs nothing. Tell the user which records will be searched and ask for approval. 2. Only after the user replied yes, call again with the same arguments and confirmed=true. The user's request is not the approval: never send confirmed=true in the same turn as the first call. Contract rules reproduced by the backend: - entity="contact" takes idsOrEmails (ctc_xxx ids or emails), entity="company" takes idsOrDomains (cpn_xxx ids or domains); or listId (clt_xxx) for a whole list of that entity, never both - At most 10000 records per call, however they are named: references (TOO_MANY_RECORDS otherwise), the members of a listId (LIST_TOO_LARGE otherwise), or the companies the domains match, since a domain matches every company carrying it (TOO_MANY_MATCHES otherwise). References matching no lemlist record come back in notFound - A record already linked to a CRM record is synced with it again instead of searched (alreadyLinked) - A record with no email, domain or LinkedIn URL cannot be searched: it is left untouched and counted in notSearchable - A record whose CRM search is already queued is not queued twice (alreadyInProgress) - Requires a connected CRM (HubSpot, Salesforce, Pipedrive, Attio, Zoho): NO_CRM_CONNECTED otherwise - Processing is ASYNCHRONOUS: the counts say what was queued, not what happened. Never present the action as done, and do not poll. When the user asks for the outcome, read it on the records: search_contacts / search_companies with the same listId and crmSyncStatus (searching = still running, synced, not_found) after a list, or after references the crmSync block search_contacts (by email or name) / search_companies (by name) return on each record. Never repeat crmSyncStatus without listId to wait for the outcome: that filter reads every record of the team
Inputs
entitystringrequired- Which lemlist records to act on: contacts or companies.one of
contactcompany confirmedboolean- REQUIRED for the action to run: true only after the user explicitly approved it. Omit or false to get the confirmation request first.
idsOrDomainsarray- entity="company" only: lemlist company ids (cpn_xxx) or domains, 10000 at most.
idsOrEmailsarray- entity="contact" only: lemlist contact ids (ctc_xxx) or email addresses, 10000 at most.
listIdstring- Acts on every record of this list (clt_xxx, from get_contact_lists / get_company_lists), instead of idsOrEmails / idsOrDomains. The list must hold the entity and 10000 records at most.
lemlistmcp_disable_campaign_sender_channelsDisable one or more channels on one sender of a campaign, and keep the person: a mailbox, a WhatsApp account, a phone number.WriteDisable Campaign Sender Channels
Disable one or more channels on one sender of a campaign, and keep the person: a mailbox, a WhatsApp account, a phone number. The other channels of the sender stay active. When the last channel leaves, the sender leaves the pool. Keywords: remove this mailbox from the campaign, stop sending from ana@old.com, drop Ana's WhatsApp on this campaign. Use when the user asks about: - Removing one channel of a sender from a campaign ("remove the mailbox ana@old.com from the Q4 campaign") - Stopping one channel type for a sender (the type without id disables every channel of the type) To remove the person, use remove_campaign_senders. Call get_campaign_senders first: it gives the channel ids of the pool. Two-phase confirmation flow: 1. First call WITHOUT confirmed: the backend counts the contacted leads that keep a sender or a channel this change removes (impact.contactedLeads). Above 0 the call writes nothing and answers status="awaiting_confirmation" with "data" (the pool after the change) and the count: show them to the user, ask with ask_question, then call again with confirmed=true and the SAME input. At 0 the call writes at once. 2. Never call with confirmed=true without explicit user approval. Contract rules reproduced by the backend: - A campaign with a dynamic sender strategy (contactOwner, leadImporter, customField:<field>) has a read-only pool that the leads fill: every pool write is refused. Switch the strategy with set_campaign_sender_strategy first, only when the user wants it. - A channel type is accepted only when a non-forced step of the campaign uses it: no WhatsApp channel without a WhatsApp step. Add the step first with add_sequence_step: it enables the type on each pool sender who owns an account of it, and lists the enabled types in senderPoolChange. - Every entry must be a team member who owns the listed channels; an alias must be one of the aliases of the mailbox. - LinkedIn has no channel to pick: a sender in the pool is enabled on LinkedIn. Never list linkedin with an id. - A pool with more than one sender needs the emailPro plan; the mailboxes above the plan allowance are dropped. - A write runs on a running campaign: the leads not contacted yet get a new sender from the pool, the contacted leads keep theirs. - Every write answers "data": the pool after the change, with the diagnostics of each sender and channel. Read them and report the errors to the user. - linkedin cannot be disabled alone: a sender in the pool is enabled on LinkedIn. Remove the sender instead.
Inputs
campaignIdstringrequired- The campaign (cam_xxx), from get_campaigns.
channelsarrayrequired- The channels to disable, applied in order, with ids from get_campaign_senders. Without id: every channel of the type. linkedin is refused: remove the sender instead.
userIdstringrequired- The sender (usr_xxx), from get_senders or get_campaign_senders.
confirmedboolean- Two-phase safety gate. Omit it (or pass false) on the first call to get the preview; pass true only after the user has explicitly approved.
lemlistmcp_disconnect_enrichment_providerRemove the team's saved API key (or ZoomInfo login) for an external enrichment provider (Lusha, Apollo, ZoomInfo): lemlist stops calling it and it is dropped from the waterfalls.WriteDisconnect Enrichment Provider
Remove the team's saved API key (or ZoomInfo login) for an external enrichment provider (Lusha, Apollo, ZoomInfo): lemlist stops calling it and it is dropped from the waterfalls. Only a connected external provider can be disconnected; anything else returns an error. Team-shared.
Inputs
providerIdstringrequired- External provider id, e.g. 'lusha' (email + phone), 'apollo' (email) or 'zoominfo' (email, OAuth only).
lemlistmcp_duplicate_campaignDuplicate a whole campaign server-side: the copy gets the same sequence, step for step, with every step's content carried over as-is.WriteDuplicate Campaign
Duplicate a whole campaign server-side: the copy gets the same sequence, step for step, with every step's content carried over as-is. ALWAYS prefer this over rebuilding a campaign step by step whenever the user wants to copy / clone / duplicate / recreate an existing campaign, or start a new one "like" an existing one (e.g. "duplicate my Q3 campaign for the DACH market", "same campaign but for CTOs"). Reading a campaign then replaying its steps through add_sequence_step drops everything the read does not surface (LinkedIn invitation notes, images, videos, A/B variants, plain-text flag). This tool copies the underlying templates, so it drops nothing. To adapt the copy afterwards, duplicate first, then edit the new campaign's steps with update_sequence_step. Never reconstruct from scratch to change a few messages. Use when the user asks about: - Duplicating / cloning / copying an existing campaign - Starting a campaign from an existing one, for another market, persona or language - Re-running a past campaign on a new audience Contract rules reproduced by the backend: - Copies the sequence, its steps and their content, plus the campaign's schedules. Does NOT copy leads: pair with copy_campaign_leads when the user wants the same audience. - Send stats start at zero on the copy, and it is named "<source name> Copy" unless you pass a name. - Requires the createCampaign permission. - The source campaign is never modified.
Inputs
campaignIdstringrequired- Campaign (cam_xxx) to duplicate. Get it from get_campaigns. Left untouched by the call.
namestring- Name of the copy (max 70 characters). Omit to let lemlist name it "<source name> Copy".
lemlistmcp_enable_campaign_sender_channelsEnable one or more channels on one sender of a campaign: a second mailbox, the WhatsApp account, a phone number.WriteEnable Campaign Sender Channels
Enable one or more channels on one sender of a campaign: a second mailbox, the WhatsApp account, a phone number. The other channels of the sender do not change. Also sets the send-as alias of a mailbox. A sender outside the pool joins it with the listed channels. Idempotent. Keywords: add Ana's WhatsApp on the campaign, send from this mailbox too, change the sending alias, send as hello@acme.com. Use when the user asks about: - Adding one channel to a sender already in the pool ("add Ana's WhatsApp on the Q4 campaign") - Changing the alias a mailbox sends from ("Ana must send from hello@acme.com"): pass the email channel with its id and the alias - Enabling every account of a type on a sender: pass the type without id, the healthy accounts are enabled Before any write: call get_senders with the campaignId. It lists every team member who can send, in the pool or not (inPool), with the user ids, the channel ids, the aliases, the health of each channel and requiredChannelTypes, the channel types that the steps require. Never guess a user id, a mailbox id, a WhatsApp account id or an alias. Load load_skill("senders") first: it tells which tool fits the request, when to change the sender strategy, and how to read the diagnostics. Contract rules reproduced by the backend: - A campaign with a dynamic sender strategy (contactOwner, leadImporter, customField:<field>) has a read-only pool that the leads fill: every pool write is refused. Switch the strategy with set_campaign_sender_strategy first, only when the user wants it. - A channel type is accepted only when a non-forced step of the campaign uses it: no WhatsApp channel without a WhatsApp step. Add the step first with add_sequence_step: it enables the type on each pool sender who owns an account of it, and lists the enabled types in senderPoolChange. - Every entry must be a team member who owns the listed channels; an alias must be one of the aliases of the mailbox. - LinkedIn has no channel to pick: a sender in the pool is enabled on LinkedIn. Never list linkedin with an id. - A pool with more than one sender needs the emailPro plan; the mailboxes above the plan allowance are dropped. - A write runs on a running campaign: the leads not contacted yet get a new sender from the pool, the contacted leads keep theirs. - Every write answers "data": the pool after the change, with the diagnostics of each sender and channel. Read them and report the errors to the user. For several senders at once, use add_campaign_senders. To remove a channel, use disable_campaign_sender_channels. No user confirmation required.
Inputs
campaignIdstringrequired- The campaign (cam_xxx), from get_campaigns.
channelsarrayrequired- The channels to enable, applied in order, with ids from get_senders. An entry with an alias on a mailbox already enabled changes the alias only.
userIdstringrequired- The sender (usr_xxx), from get_senders or get_campaign_senders.
lemlistmcp_enrich_leadEnrich existing campaign lead.WriteEnrich Lead
Enrich existing campaign lead. ASYNC — poll with bulk_get_enrichment_results (pass enrichmentIds: [id]). For non-campaign contacts use bulk_enrich_data. ALL options COST CREDITS.
Inputs
leadIdstringrequired- The lead ID to enrich (lea_xxx format)
findEmailboolean- ⚠️ COSTS CREDITS: Find email from lead data
findPhoneboolean- ⚠️ COSTS CREDITS: Find phone number
linkedinEnrichmentboolean- ⚠️ COSTS CREDITS: Enrich with LinkedIn data
verifyEmailboolean- ⚠️ COSTS CREDITS: Verify lead email
lemlistmcp_export_people_database_csvExport a People Database search as CSV and return a download link.WriteExport People Database Csv
Export a People Database search as CSV and return a download link. Use it when: - The user asks to export, download or "get a CSV of" People Database results. - They want the full rows (name, company, title, country, headcount…) rather than a count or an on-screen table. Do NOT use it in the lemlist chat, where export_data covers the same export and renders a real download button — this tool only returns a link, which is the best an external client can show. Contract rules reproduced by the backend: - filters must come from people_database_nl_to_filters — every filterId is checked against the registry. - Capped at 1000 rows; a higher limit is rejected. Free-trial teams are capped lower, at one page. - The link carries a short-lived token and expires. Hand it to the user as-is — do not fetch it yourself, the CSV is meant for their disk, not for this conversation.
Inputs
filtersarrayrequired- The search to export — the same "filters" array people_database_nl_to_filters returns.
modestringrequired- What to export: 'leads' for people, 'companies' for companies. Must match the mode the filters were built for.one of
leadscompanies limitinteger- How many rows to export, at most 1000. Defaults to that cap.
lemlistmcp_generate_campaign_for_watch_listGenerate an AI-powered outreach campaign for an existing watch list.WriteGenerate Campaign For Watch List
Generate an AI-powered outreach campaign for an existing watch list. Builds a campaign from a predefined sequence template, writes every step's copy with AI (steered by copyStyle and the watch list's signal), links it to the watch list, and returns the campaign with its generated sequences. Use when the user asks about: - Turning a watch list's signals into a ready-to-run outreach campaign - Auto-generating a sequence + AI copy targeted at what the watch list monitors Flow: - Run it on an existing watch list, or create one first (create_watch_list), then call this tool to generate its campaign. - autoLaunch=true launches the campaign immediately; false leaves it in draft. Important: - The watch list (watchListId) must exist for the current team. - reviewBeforeSend only applies when autoLaunch=true.
Inputs
autoLaunchbooleanrequired- Launch the campaign immediately; false creates it in draft
autoRunbooleanrequired- Generate an icebreaker AI variable automatically for each new lead
copyStylestringrequired- Tone of the AI-generated copy: direct | conversational | insight_ledone of
directconversationalinsight_led reviewBeforeSendbooleanrequired- Mark the sequence's first message step as a manual task, reviewed before sending (ignored when autoLaunch=false)
sequenceTypestringrequired- Predefined sequence recipe: email | linkedin | multichannelone of
emaillinkedinmultichannel watchListIdstringrequired- Source watch list ID (wat_xxx) to build the campaign from
personaIdstring- Persona (pdp_xxx, leads mode) set on the watch list to find contacts at the companies it monitors. Required on a company watch list whose signal type carries no contact and that has no persona yet: its signals bring no one to the campaign otherwise. Rejected on a signal type that ships its own contact ("newHire", "promotion"). Replaces the persona the watch list already has. Get it from list_people_database_personas; never invent one.
lemlistmcp_get_inbox_conversationGet the full conversation thread for a specific contact across all channels (email, LinkedIn, WhatsApp, SMS).WriteGet Inbox Conversation
Get the full conversation thread for a specific contact across all channels (email, LinkedIn, WhatsApp, SMS).
Inputs
contactIdstringrequired- The contact ID (ctc_xxx format) to get the conversation thread for. Do not use leadId or any other ID type.
fullBodyboolean- Return each email body as the FULL original HTML, untrimmed and with the length cap disabled (LinkedIn/WhatsApp/SMS bodies are unchanged). Default false. This can make the response very large — set true ONLY when the user explicitly asks to read a message's full or original content (its formatting, images, or the complete text a truncated plain-text body cut off). For summaries, triage, sentiment, or "what did they say", leave it false and use the trimmed plain-text bodies.default
false limitnumber- Number of activities per page (default and max: 10). Use 'page' to load older messages.default
10 markAsReadboolean- Whether to mark the conversation as read (default: false)default
false pagenumber- Page number for pagination (default: 1)default
1
lemlistmcp_import_crm_leads_to_campaignImport the records of a CRM filter (HubSpot list, Salesforce list view or report, Pipedrive filter, Attio People view) into a campaign as leads.WriteImport Crm Leads To Campaign
Import the records of a CRM filter (HubSpot list, Salesforce list view or report, Pipedrive filter, Attio People view) into a campaign as leads. Keywords: import leads from CRM, sync HubSpot list to campaign, Salesforce report to campaign, Pipedrive filter to campaign, Attio view to campaign. Use when the user asks about: - Importing / syncing leads from their CRM into a campaign that has no CRM filter yet. Call get_crm_import_filters first to get the filterId. Salesforce list views scoped to their owner ("My leads", "My contacts"…) are read as the team CRM connection's user by default. Pass userId only when the user explicitly asks to read such a view as a given teammate, and only with an id from ownerScopeUsers (get_crm_import_filters). Never infer that a view is owner-scoped from its name, and never offer team members outside ownerScopeUsers. Two-phase confirmation flow: 1. First call WITHOUT userConfirmed: nothing is written, checks the filter exists in the CRM and returns status="awaiting_confirmation" with the campaign, the filter name and the options. Show that summary to the user. 2. Once the user has explicitly approved, call again with userConfirmed=true. Contract rules: - The import is RECURRING: the campaign stays linked to the filter and new records the filter picks up are imported automatically. - Only a campaign WITHOUT a CRM filter can be linked. Changing or unlinking a campaign's CRM filter is done in the lemlist app: tell the user so. - The first import runs in the background: leads appear over the next minutes, not in this response. - Enrichments run on every imported lead, now and later, charged to the calling user. Credit cost, charged only on success — a lookup that returns nothing costs 0: find_email 5 per email found, find_phone 20 per phone number found, verify 1 per email verified, linkedin_enrichment 1 per profile enriched. Quote this to the user before confirming.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx) to import into.
filterIdstringrequired- Filter id returned by get_crm_import_filters.
deduplicatestring- "all" skips contacts already in another campaign, "activeOnly" skips only contacts still active in another campaign, "off" (default) imports everyone.one of
offallactiveOnly enrichFeaturesarray- Enrichments to run on every imported lead. COSTS CREDITS, per lead.
filterTypestring- Salesforce only: "lead" or "contact" list views, or "report". Ignored by HubSpot, Pipedrive and Attio. Defaults to "lead" when the Salesforce connection allows Leads, "contact" otherwise.one of
leadcontactreport userConfirmedboolean- Set true ONLY after the user has explicitly approved the import summary.
userIdstring- Salesforce ONLY: the team member a "My …" list view is read as. Must be the id of one of the ownerScopeUsers returned by get_crm_import_filters. Leave it out unless the user explicitly asks to read a "My …" view as a given teammate.
lemlistmcp_launch_campaignLaunch a DRAFT campaign: review and send its already-scanned leads now, and optionally enable auto-review so future leads launch on their own.WriteLaunch Campaign
Launch a DRAFT campaign: review and send its already-scanned leads now, and optionally enable auto-review so future leads launch on their own. Keywords: launch campaign, start draft, review scanned leads, go live, send campaign. Use when the user asks about: - Launching / going live with a draft campaign that has leads ready to send. - Reviewing and sending the scanned leads sitting in a campaign. - Making a campaign send new leads automatically as they are added (auto-review). To resume a PAUSED campaign use set_campaign_state (action "start"); this tool is for a draft that has never sent. In an interactive UI, prefer review_and_launch_leads so the user can review leads visually; use this tool for auto-review, large backlogs (>100 scanned), or a non-interactive launch. Two-phase confirmation flow: 1. First call WITHOUT userConfirmed (or userConfirmed=false): returns status="awaiting_confirmation". Ask the user to confirm launching, and also ask whether to enable auto-review for future leads, then call again. 2. Once the user has explicitly approved, call again with userConfirmed=true and autoReview set to the user's choice to review and send. Contract rules: - Sends real messages to leads — requires explicit user confirmation. - Launching always reviews and sends the current scanned backlog, regardless of autoReview. - autoReview is an explicit, user-chosen option that governs ONLY future leads; it defaults to off. Ask the user before enabling it. - Only leads in the "scanned" state are launched; leads already reviewed are left untouched (idempotent on re-run). - Leads still enriching or missing required AI variables are skipped, not sent. - The campaign must have a sequence and no blocking step errors.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx) of the draft to launch.
autoReviewboolean- Whether FUTURE leads added to the campaign send automatically without manual review. This does NOT change what launches now — the current scanned leads send either way. Omit or false = future leads wait in "scanned" for manual review. Ask the user which they want before setting it.
userConfirmedboolean- Set true ONLY after the user has explicitly approved launching. Launching sends real messages.
lemlistmcp_load_saved_searchLoad one saved People Database search by id and return its name, mode and filters so you can re-run it.WriteLoad Saved Search
Load one saved People Database search by id and return its name, mode and filters so you can re-run it. Use when the user asks about: - Reloading a specific saved search ("load my <name> search", "reopen that list"), after list_saved_searches resolved its id The returned filters use the lemleads_search shape ({ filterId, in, out }): re-run the search with lemleads_search using them and the returned mode. An unknown id fails as not found.
Inputs
savedSearchIdstringrequired- The saved search id (lss_xxx), from list_saved_searches or save_people_db_search
lemlistmcp_merge_companiesREQUIRES CONFIRMATION: Merge up to 10 lemlist companys into one.WriteMerge Companies
REQUIRES CONFIRMATION: Merge up to 10 lemlist companys into one. The survivor (primaryId) keeps its id and every value it holds; its empty fields are filled from the other companys, which are then deleted. Everything attached to a deleted company (activities, tasks, list memberships, contacts) is moved to the survivor. When a CRM is connected, the survivor keeps its CRM link (or adopts the first linked company's when it has none) and receives the merged values there; lemlist never merges or deletes anything in the CRM itself, so the other companys' CRM records stay in the CRM, unlinked from lemlist. Use when the user asks about: - Deduplicating companys that describe the same organization - Cleaning up duplicates after an import or a CRM sync Two-phase confirmation flow: 1. First call WITHOUT confirmed (or confirmed=false): returns the resolved records with their names and status="awaiting_confirmation". Show them to the user and ask which one must survive. 2. Once the user has explicitly approved, call again with confirmed=true to run the merge. Contract rules reproduced by the backend: - A company is addressed by its lemlist id (cpn_xxx). - primaryId must not appear in secondaryIds, and each id is listed once - A company being enriched cannot be merged yet (ENTITY_MERGE_ENRICHMENT_IN_PROGRESS): retry once it finishes - The merge folds one record at a time and STOPS at the first failure: mergedIds are done, remainingIds still exist - There is no undo: deleted companys are gone
Inputs
primaryIdstringrequired- The surviving record — keeps its id and its values. A lemlist id (ctc_xxx / cpn_xxx); contacts also accept one of their email addresses.
secondaryIdsarrayrequired- The records folded into the survivor then deleted, 9 at most. Same addressing as primaryId.
confirmedboolean- REQUIRED for the merge to run: true only after the user explicitly approved, with the records named. Omit or false to get the records to confirm first.
lemlistmcp_merge_contactsREQUIRES CONFIRMATION: Merge up to 10 lemlist contacts into one.WriteMerge Contacts
REQUIRES CONFIRMATION: Merge up to 10 lemlist contacts into one. The survivor (primaryId) keeps its id and every value it holds; its empty fields are filled from the other contacts, which are then deleted. Everything attached to a deleted contact (activities, tasks, list memberships, campaign leads) is moved to the survivor. When a CRM is connected, the survivor keeps its CRM link (or adopts the first linked contact's when it has none) and receives the merged values there; lemlist never merges or deletes anything in the CRM itself, so the other contacts' CRM records stay in the CRM, unlinked from lemlist. Use when the user asks about: - Deduplicating contacts that describe the same person - Cleaning up duplicates after an import or a CRM sync Two-phase confirmation flow: 1. First call WITHOUT confirmed (or confirmed=false): returns the resolved records with their names and status="awaiting_confirmation". Show them to the user and ask which one must survive. 2. Once the user has explicitly approved, call again with confirmed=true to run the merge. Contract rules reproduced by the backend: - A contact is addressed by its lemlist id (ctc_xxx) or by one of its email addresses. - primaryId must not appear in secondaryIds, and each id is listed once - Email addresses: the survivor keeps its own; a loser's addresses are dropped, unless the survivor has none (then it takes the first loser's) - A contact being enriched cannot be merged yet (ENTITY_MERGE_ENRICHMENT_IN_PROGRESS): retry once it finishes - The merge folds one record at a time and STOPS at the first failure: mergedIds are done, remainingIds still exist - There is no undo: deleted contacts are gone
Inputs
primaryIdstringrequired- The surviving record — keeps its id and its values. A lemlist id (ctc_xxx / cpn_xxx); contacts also accept one of their email addresses.
secondaryIdsarrayrequired- The records folded into the survivor then deleted, 9 at most. Same addressing as primaryId.
confirmedboolean- REQUIRED for the merge to run: true only after the user explicitly approved, with the records named. Omit or false to get the records to confirm first.
lemlistmcp_move_campaign_folderMove a campaign folder (and its whole subtree) under a different parent folder, or to the root.WriteMove Campaign Folder
Move a campaign folder (and its whole subtree) under a different parent folder, or to the root. Use when the user asks about: - Reorganizing their folder hierarchy. - Moving a folder out of its current parent (omit newParentId for the root). Contract rules reproduced by the backend: - A folder cannot be moved into itself or one of its own sub-folders. - The resulting depth must stay within 15 levels, and the target level within 50 folders. - The folder name must stay unique at the target level.
Inputs
folderIdstringrequired- Folder ID (cfo_xxx) to move.
newParentIdstring- Target parent folder ID (cfo_xxx). Omit to move the folder to the root.
lemlistmcp_move_campaigns_to_folderMove one or more campaigns into a folder, or out to the root (the drag-and-drop equivalent).WriteMove Campaigns To Folder
Move one or more campaigns into a folder, or out to the root (the drag-and-drop equivalent). Use when the user asks about: - Filing campaigns into a folder. - Taking campaigns out of a folder (omit folderId to move them to the root). Contract rules reproduced by the backend: - Only campaigns owned by the current team are moved; unknown ids are skipped.
Inputs
campaignIdsarrayrequired- Campaign IDs (cam_xxx) to move (1-100). Pass every campaign in one call.
folderIdstring- Target folder ID (cfo_xxx) to file the campaigns into. Omit to move them out to the root.
lemlistmcp_optimize_watch_listsGenerate AI optimization recommendations for the team's existing watch lists.WriteOptimize Watch Lists
Generate AI optimization recommendations for the team's existing watch lists. Analyzes active watch lists and returns concrete, ready-to-apply changes — each a filter diff (add/remove) on an existing watch list, with a short title and a rationale — tailored to the team's ICP and the user's stated goal. Use when the user asks about: - Improving, tuning or fixing the signal agents (watch lists) they already have - Why an agent returns too few, too many, or low-quality signals Important: - Requires the team's business context (AI Context Center) to be set. - watchListIds is required: call list_watch_lists first to get the team's active agents, then pass the ids of the ones to optimize. - Returns recommendations only; applying them is a separate step.
Inputs
reasonstringrequired- What the user wants to improve about their watch lists (synthesized from the clarifying questions you asked first). Steers which optimizations the analyzer proposes.
watchListIdsarrayrequired- Ids of the watch lists (agents) to optimize. Required: call list_watch_lists first to list the team's active agents, then pass the ids of the ones to optimize.
lemlistmcp_personalize_step_for_leadPersonalize one step of a campaign sequence for a single lead: the lead receives this version of the message instead of the step's shared content, which stays untouched for every other lead.WritePersonalize Step For Lead
Personalize one step of a campaign sequence for a single lead: the lead receives this version of the message instead of the step's shared content, which stays untouched for every other lead. Same as "Personalize" in the lemlist review page. Use when the user asks to: - Rewrite or tweak an upcoming email, LinkedIn, SMS or WhatsApp message for one prospect before it is sent - Put one lead on version A or B of an A/B tested step Typical flow: get_campaigns -> search_campaign_leads (lead id) -> get_campaign_sequences (step id and current content) -> personalize_step_for_lead. Contract rules reproduced by the backend: - Supported steps: email, whatsappMessage, sms, linkedinInvite, linkedinSend, linkedinInMail. Not voice notes, not conditions. - Every content field is optional and overlays the lead's current version (its personalization, else the step's content); fields you omit are kept. Calling with only variant puts the lead on that version as is. - A personalized step is no longer A/B tested for that lead: it pins the lead to the variant it is written on. Switching the variant replaces the lead's personalization of the other variant. - Refused once the lead has reached the step (sent, skipped or ignored). For a task already created, use update_task_content on that task instead. - On a follow-up email, a non-empty subject starts a new email thread; omit subject to stay in the thread. - To undo, use reset_step_personalization.
Inputs
campaignIdstringrequired- Campaign the lead belongs to (cam_xxx).
leadIdstringrequired- Lead (lea_xxx), from search_campaign_leads on that campaign.
stepIdstringrequired- Step (stp_xxx), from get_campaign_sequences on that campaign.
altMessagestring- linkedinInvite and linkedinSend only. The connection-request note (max 300 characters on an invitation); cannot be empty.
altMessagePremiumstring- linkedinSend only. The note sent from a premium account (max 300 characters); cannot be empty.
ccarray- email only. Cc addresses for this lead; [] removes the cc.
messagestring- The message body for this lead. Plain text; line breaks are kept. Omit it to keep the current body.
plainTextboolean- email only. Send this lead a plain-text email.
subjectstring- email and linkedinInMail only. The subject for this lead. Omit it to keep the current one; on a follow-up email a non-empty subject starts a NEW thread. Cannot be blank on linkedinInMail (REVIEW_DRAFT_SUBJECT_EMPTY).
variantstring- A/B variant to write on (A or B). Omit it to keep the variant the lead is already on.one of
AB
lemlistmcp_push_companies_from_people_databasePush companies from the People Database into your CRM (optionally into a company list).WritePush Companies From People Database
Push companies from the People Database into your CRM (optionally into a company list). Use when the user asks about: - Saving companies found in the People Database as CRM companies - Bulk-adding People Database companies to a company list - Importing the companies returned by display_companies or lemleads_search (companies mode) Contract rules reproduced by the backend: - pass ONE source: companyIds (an explicit selection), dataRef (a prior companies search) or filters - company ids are CoreSignal ids from lemleads_search (companies mode) or display_companies, never cpn_xxx - dataRef comes from a lemleads_search in companies mode; the backend pushes its matches up to the cap - filters are People Database company filters (from get_lemleads_filters) for a search you build directly - excludeCompanyIds drops specific companies from the set (e.g. "all these except X") - companies are matched and de-duplicated by CoreSignal id, so re-pushing never duplicates one - listId comes from get_company_lists; it must be a non-dynamic company list — a contact list is rejected - at most 1000 companies per call; a bigger search reports how many matched - to push People Database people to contacts instead, use push_contacts_from_people_database
Inputs
companyIdsarray- People Database company ids to push (digits only, max 1000). CoreSignal ids from lemleads_search (companies mode) or display_companies — not cpn_xxx workspace ids. Use for an explicit selection; to push a whole search, pass dataRef instead.
dataRefstring- Reference returned by a lemleads_search in companies mode. Pushes that whole result set (up to 1000) without listing ids — preferred when you already ran the search.
excludeCompanyIdsarray- CoreSignal company ids (digits only, max 1000) to drop from the resolved set (e.g. "push all of these except X"). Excluded inside the search.
filtersarray- People Database company filters (filterId/in/out, from get_lemleads_filters). Pushes every company matching them (up to 1000) — build a search directly instead of via dataRef. Pass one of companyIds, dataRef or filters.
listIdstring- Target company list id (clt_xxx format). Get it from get_company_lists. Omit to import into the CRM without adding to a list.
lemlistmcp_push_contacts_from_people_databasePush leads from the People Database to your contacts (CRM).WritePush Contacts From People Database
Push leads from the People Database to your contacts (CRM). **When to use:** - User wants to save leads from People Database as contacts - After displaying leads with display_leads, user wants to import selected leads **Parameters:** - profileIds: Lead IDs from the People Database (required, max 1,000) - enrichFeatures: Optional enrichment — 'find_email' and/or 'find_phone' (⚠️ COSTS CREDITS) - listId: Target contact list ID in clt_xxx format (optional). OMIT to push to "all contacts" — do NOT pass strings like "all" or "default", they will be rejected. **IMPORTANT:** - ⚠️ Credit cost, charged only on success — a lookup that returns nothing costs 0: find_email 5 per email found, find_phone 20 per phone number found. Multiply by the number of profileIds to quote a maximum before enabling them - Maximum 1,000 leads per request - To get lead IDs from workspace selection, call get_workspace_items first - To get a valid listId, call get_contact_lists first — never invent or guess a list ID **Example:** { "profileIds": ["12345", "67890"], "listId": "clt_abc123def456ghi", "enrichFeatures": ["find_email"] }
Inputs
profileIdsarrayrequired- People Database lead IDs to push to contacts (max 1,000)
enrichFeaturesarray- ⚠️ COSTS CREDITS: Enrichment features to apply when pushing leads
listIdstring- Target contact list ID in clt_xxx format. Omit to push to "all contacts". Get valid IDs from get_contact_lists — never invent or pass aliases like "all".
lemlistmcp_read_help_center_articleRead ONE lemlist help center article in full.WriteRead Help Center Article
Read ONE lemlist help center article in full. search_help_center only ever returns part of an article; this returns the whole article. Use when: - A search hit came back with partial=true and the answer is not in the part you got - You are about to state a COUNT, a LIST or a TABLE from an article (supported countries, plans, limits, pricing, steps): an excerpt that stops mid-table is NOT the whole table - You are about to tell the user that a capability is unsupported on the strength of an article Contract rules reproduced by the backend: - articleId comes from search_help_center. Never invent one. - found=false means no published article matches that id — search again rather than answering from memory. - truncated=true on the result means even the full read hit the size cap, so the end of the article is still missing: say so instead of presenting what you have as complete.
Inputs
articleIdstringrequired- The articleId of a search_help_center hit. The article URL (or the number inside it) is accepted too.
lemlistmcp_rename_campaign_folderRename a campaign folder and/or change its color.WriteRename Campaign Folder
Rename a campaign folder and/or change its color. Use when the user asks about: - Renaming a folder. - Changing a folder's color. Contract rules reproduced by the backend: - The new name must be unique among folders that share the same parent.
Inputs
folderIdstringrequired- Folder ID (cfo_xxx) to update.
colorstring- New folder color (hex).one of
#F87171#FB923C#FBBF24#A3E635#34D399#60A5FA#A78BFA#F472B6#9CA3AF namestring- New folder name. Must stay unique among folders sharing the same parent.
lemlistmcp_replace_campaign_sendersReplace the whole sender pool of a campaign with the listed senders and channels.WriteReplace Campaign Senders
Replace the whole sender pool of a campaign with the listed senders and channels. Every sender and channel outside the list leaves the pool; an empty list clears the pool. Keywords: set the senders, only Ana and Ben send this campaign, nobody else, reset the senders, clear the senders. Use when the user asks about: - Defining the complete list of senders ("Ana and Ben send the Q4 campaign, nobody else") - Clearing the pool of a campaign Use it only when the user names the whole list. For one change on the pool, prefer add_campaign_senders or remove_campaign_senders: a replace that forgets a current sender removes it silently. When only a channel of one sender changes, prefer enable_campaign_sender_channels or disable_campaign_sender_channels. Call get_campaign_senders first to know the current pool and tell the user who leaves. Before any write: call get_senders with the campaignId. It lists every team member who can send, in the pool or not (inPool), with the user ids, the channel ids, the aliases, the health of each channel and requiredChannelTypes, the channel types that the steps require. Never guess a user id, a mailbox id, a WhatsApp account id or an alias. Load load_skill("senders") first: it tells which tool fits the request, when to change the sender strategy, and how to read the diagnostics. Two-phase confirmation flow: 1. First call WITHOUT confirmed: the backend counts the contacted leads that keep a sender or a channel this change removes (impact.contactedLeads). Above 0 the call writes nothing and answers status="awaiting_confirmation" with "data" (the pool after the change) and the count: show them to the user, ask with ask_question, then call again with confirmed=true and the SAME input. At 0 the call writes at once. 2. Never call with confirmed=true without explicit user approval. Contract rules reproduced by the backend: - A campaign with a dynamic sender strategy (contactOwner, leadImporter, customField:<field>) has a read-only pool that the leads fill: every pool write is refused. Switch the strategy with set_campaign_sender_strategy first, only when the user wants it. - A channel type is accepted only when a non-forced step of the campaign uses it: no WhatsApp channel without a WhatsApp step. Add the step first with add_sequence_step: it enables the type on each pool sender who owns an account of it, and lists the enabled types in senderPoolChange. - Every entry must be a team member who owns the listed channels; an alias must be one of the aliases of the mailbox. - LinkedIn has no channel to pick: a sender in the pool is enabled on LinkedIn. Never list linkedin with an id. - A pool with more than one sender needs the emailPro plan; the mailboxes above the plan allowance are dropped. - A write runs on a running campaign: the leads not contacted yet get a new sender from the pool, the contacted leads keep theirs. - Every write answers "data": the pool after the change, with the diagnostics of each sender and channel. Read them and report the errors to the user. - Each entry resolves as an addition to an empty pool: an entry without channels gets the required channel types, healthy first.
Inputs
campaignIdstringrequired- The campaign (cam_xxx), from get_campaigns.
sendersarrayrequired- The whole new pool, with user ids and channel ids from get_senders. An empty array clears the pool.
confirmedboolean- Two-phase safety gate. Omit it (or pass false) on the first call to get the preview; pass true only after the user has explicitly approved.
lemlistmcp_reset_enrichment_waterfallsReset one or more default waterfalls (default_email, default_phone) back to lemlist's managed behaviour in ONE call: lemlist picks the provider order again, as if nobody had edited it.WriteReset Enrichment Waterfalls
Reset one or more default waterfalls (default_email, default_phone) back to lemlist's managed behaviour in ONE call: lemlist picks the provider order again, as if nobody had edited it. Only default waterfalls can be reset; a custom id returns an error for that entry. Ids are applied independently: 'results' reports the reset waterfall or an error per waterfallId. Team-shared and free (no credits).
Inputs
waterfallIdsarrayrequired- Default waterfall ids to reset (default_email / default_phone).
lemlistmcp_reset_step_personalizationRemove a lead's personalization of one campaign step: the lead receives the step's shared message again, and A/B testing applies to it again.WriteReset Step Personalization
Remove a lead's personalization of one campaign step: the lead receives the step's shared message again, and A/B testing applies to it again. Same as "Reset override" in the lemlist review page. Use when the user asks to: - Undo a personalization made with personalize_step_for_lead or in the review page - Put a lead back on the campaign's standard message for a step Contract rules reproduced by the backend: - The personalized text is deleted and cannot be recovered: confirm with the user first. - Refused once the lead has reached the step (sent, skipped or ignored). - Idempotent: reset=false when the step was not personalized for that lead.
Inputs
campaignIdstringrequired- Campaign the lead belongs to (cam_xxx).
leadIdstringrequired- Lead (lea_xxx), from search_campaign_leads on that campaign.
stepIdstringrequired- Step (stp_xxx), from get_campaign_sequences on that campaign.
lemlistmcp_run_inbox_placement_testRun an inbox placement (spam) test: send one email from a mailbox to ~25 seed inboxes across Google, Microsoft, and SMTP, to measure where it lands (inbox / promotions / spam) per provider.WriteRun Inbox Placement Test
Run an inbox placement (spam) test: send one email from a mailbox to ~25 seed inboxes across Google, Microsoft, and SMTP, to measure where it lands (inbox / promotions / spam) per provider. Use when the user wants to: - Test whether a campaign's content lands in spam (Phase 6 of the deliverability-audit skill) - Compare placement of two campaigns to quantify a content change - Diagnose a "Google fine but SMTP spam" content issue This SENDS A REAL EMAIL and consumes 100 credits. Confirm with the user first. Pull the real campaign subject + body (get_campaign_sequences / preview_email) so the test mirrors production content. The test is asynchronous: this returns a testId with state "pending". Poll get_inbox_placement_result with that testId until it reports "completed" to read the per-provider breakdown.
Inputs
messagestringrequired- Email body to test (use the real campaign content)
subjectstringrequired- Subject line to test (use the real campaign subject)
userMailboxIdstringrequired- Sender mailbox (usm_xxx) the test email is sent from — its reputation is what gets measured
campaignIdstring- Optional campaign (cam_xxx) this test is linked to, for reporting
stepIdstring- Optional sequence step (stp_xxx) the content comes from
lemlistmcp_save_memorySave a piece of information to persistent memory so it can be recalled in future conversations.WriteSave Memory
Save a piece of information to persistent memory so it can be recalled in future conversations.
Inputs
contentstringrequired- The information to remember (max 5000 chars, in the user language)
topicstringrequired- Unique topic key in snake_case english (e.g. "preferred_tone", "icp_details", "campaign_naming")
scopestring- Scope: "user" (default, personal) or "team" (shared with all team members)one of
userteam
lemlistmcp_save_people_db_searchSave a People Database search (its filters) under a name so the user can reload it later, from list_saved_searches / load_saved_search or from the People Database "Saved searches" menu.WriteSave People Db Search
Save a People Database search (its filters) under a name so the user can reload it later, from list_saved_searches / load_saved_search or from the People Database "Saved searches" menu. Use when the user asks about: - Saving, bookmarking, naming or "keeping" a lead or company search they just ran ("save this search", "I'll need this list again later") Contract rules reproduced by the backend: - Pass the SAME filters and mode you used in lemleads_search; at least one filter is required - Only filter-based searches can be saved: a description/keywords vector search has no saved representation - Every filterId must be usable in the People Database UI (from get_lemleads_filters); others are rejected by name - name must be unique within the team (duplicates are rejected: ask the user for another name) - Saved searches are team-scoped and persist across sessions. Do NOT use save_memory for this. Returns the created saved search id (lss_xxx).
Inputs
filtersarrayrequired- People Database filters to save (same shape as lemleads_search). At least one.
modestringrequired- Search mode: "people" for leads, "companies" for organizationsone of
peoplecompanies namestringrequired- Name shown in the People Database "Saved searches" menu (unique within the team)
lemlistmcp_set_ab_variantSet or update the A/B test variant B (subject and/or body) of an EMAIL sequence step.WriteSet Ab Variant
Set or update the A/B test variant B (subject and/or body) of an EMAIL sequence step. Use when the user asks to: - A/B test an email step (two subjects, two bodies, or both) - Add or edit the variant B of an email step Behaviour: - If the step has no A/B test yet, this enables it: variant B starts as a copy of variant A (same subject, body, CC), then the fields you pass are applied on top. Fields you omit stay inherited from A. - If a variant B already exists, only the fields you pass are changed; the rest of B is left untouched. - Pass no field to just enable A/B with variant B identical to A. Scope: this tool covers email steps only, which is NOT the same as "A/B testing is email-only". - LinkedIn invitation (e.g. variant A with a note vs variant B without), LinkedIn chat message, LinkedIn InMail, SMS and WhatsApp chat message steps all support A/B testing in the product; no tool exposes it yet. - For those channels, answer that it is possible and hand over the manual path: open the campaign in the sequence editor, select the step, turn on its A/B test, then edit variant B there. - Never tell the user that a non-email step cannot be A/B tested. Contract rules reproduced by the backend: - Calling this tool on a step that is not an email step is rejected (SEQUENCE_AB_ONLY_EMAIL). The public REST A/B endpoint carries the same limit. - A running campaign can reject edits to steps that have already launched: pause it first.
Inputs
sequenceIdstringrequired- Sequence ID (seq_xxx) that contains the step.
stepIdstringrequired- Step ID (stp_xxx) to set variant B on.
altMessagestring- Depends on the step type. On an email step: the plain-text alternative body, rarely needed. On a linkedinInvite step: the PREMIUM invitation note (max 300 characters), used instead of "message" when the sender has LinkedIn Premium. Omit to inherit from A or keep B unchanged.
altMessagePremiumstring- linkedinSend steps only: the PREMIUM variant of variant B's out-of-network note (max 300 characters), used instead of "altMessage" when the sending LinkedIn account is Premium or Sales Navigator. Omit to inherit from A or keep B unchanged.
ccarray- Variant-B CC recipients (email addresses or {{variables}}). Email steps only. Omit to inherit from A or keep B unchanged.
messagestring- Variant-B message: the HTML body on an email step, the message text on a LinkedIn step. Pass an empty string ONLY on a linkedinInvite step, where it means "send the invitation without a note" (rejected on every other step type, which the send cron would read as a broken step). Omit to inherit variant A's message (on creation) or keep B unchanged (on update).
plainTextboolean- Send variant B as plain text instead of HTML. Email steps only. Omit to inherit from A or keep B unchanged.
subjectstring- Variant-B subject line. Email steps only. Omit to inherit variant A's subject (on creation) or keep B unchanged (on update). Pass an empty string to remove it: variant B then replies on the thread of the previous email, like variant A would.
lemlistmcp_set_campaign_sender_strategySet the sender-assignment strategy for a campaign — the algorithm that decides which sender (user) is attached to each lead at launch.WriteSet Campaign Sender Strategy
Set the sender-assignment strategy for a campaign — the algorithm that decides which sender (user) is attached to each lead at launch. Keywords: sender strategy, dynamic sender assignment, round-robin sender, contact owner sender, custom variable sender, per-lead sender routing. Use when the user asks about: - Configuring HOW senders are assigned to leads (as opposed to WHICH users are in the send pool — that is set_campaign_senders). - Routing sends by a per-lead custom variable (e.g. "send from the account manager listed on each lead"). - Using the CRM contact owner as the sender. - Using the user who imported the lead as the sender. - Switching between random / round-robin and any of the dynamic strategies above. This tool decides HOW senders are picked. To change WHO is in the eligible pool, use set_campaign_senders. Contract rules: - "customField:<fieldName>" requires the field to exist as a team custom field of type "user", AND each lead must have variables.<fieldName> populated with a valid userId. Leads missing that value are silently dropped from launch with a "noAssignment" diagnostic (they will not be sent to). - Changing the strategy on a running campaign does NOT reassign already-resolved leads; it only applies to leads reviewed/launched after the change. - No user confirmation required.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx) to update.
senderStrategystringrequired- How senders are picked for each lead at launch. One of: "random" (round-robin over the campaign send pool), "contactOwner" (use each lead's CRM contact owner), "leadImporter" (use the user who imported the lead), or "customField:<fieldName>" (read the sender userId from lead.variables.<fieldName>).
lemlistmcp_set_sequence_step_senderForce the sender of one sequence step, instead of the campaign sender pool.WriteSet Sequence Step Sender
Force the sender of one sequence step, instead of the campaign sender pool. Use when the user asks to: - Send one step from a specific team member or a specific mailbox, WhatsApp account or phone number - Change the sender of a LinkedIn or WhatsApp voice note step - Make one step always come from the same person, whatever sender the lead got Contract rules reproduced by the backend: - Email, LinkedIn, WhatsApp, SMS, call, manual, API and phone steps accept a forced sender. Delay, condition and other utility steps do not. - channel is required on an email, LinkedIn, WhatsApp or SMS step, and channel.type must match the step. channel is forbidden on a call, manual, API or phone step. - The user must be a team member who owns the account. An unhealthy account is accepted: the result carries its diagnostics. - An InMail step needs a sender with LinkedIn Premium. - The pool changes as in the campaign editor: the channels of a type that no other step uses are disabled. senderPoolChange lists the channel types that the change enabled or disabled. - On a running campaign, the pending tasks of the step move to the forced sender. - Get the step ids from get_campaign_sequences and the user ids from get_users.
- Idempotent
Inputs
sequenceIdstringrequired- Sequence ID (seq_xxx) that contains the step.
stepIdstringrequired- Step ID (stp_xxx) to pin the sender on.
userIdstringrequired- Team member (usr_xxx) who sends the step. Get the ids from get_users (userIds: ["all"]).
channelobject- REQUIRED on an email, LinkedIn, WhatsApp or SMS step. FORBIDDEN on a call, manual, API or phone step: those steps pin a user, not an account.
lemlistmcp_skip_step_for_everyoneSkip one step of a campaign sequence for EVERY lead: leads that have not reached it move on to the following step after that step's own delay, leads added to the campaign later never receive it, and the skipped step is recorded as "Step skipped" in the review and the reports.WriteSkip Step For Everyone
Skip one step of a campaign sequence for EVERY lead: leads that have not reached it move on to the following step after that step's own delay, leads added to the campaign later never receive it, and the skipped step is recorded as "Step skipped" in the review and the reports. Campaign-wide and permanent. Use when the user asks to: - Drop a step from a running campaign for everyone (it no longer fits, it under-performs, the channel is not wanted anymore) without pausing or rebuilding the campaign - Free the sending capacity a step consumes, for all leads at once Two-phase confirmation flow (mandatory): 1. First call WITHOUT confirmed: returns status="awaiting_confirmation" with leadsImpacted (an upper bound: every lead not yet past the step) and destination (the step they continue on, or completed=true when it is the last one). Show both to the user and get explicit approval. 2. Once approved, call again with confirmed=true to skip. Typical flow: get_campaigns -> get_campaign_sequences (read the step ids) -> skip_step_for_everyone (preview) -> ask the user -> skip_step_for_everyone (confirmed=true). Contract rules reproduced by the backend: - A condition step is never skippable. - Leads that already ran the step are not affected; nothing already sent changes. - Idempotent: a step already skipped for everyone answers skipped=false (and the preview says alreadySkipped=true) rather than failing. - A skip cannot be undone, here or in the lemlist UI. To skip a step for one lead or a list of leads only, use skip_step_for_lead. - Never call with confirmed=true without explicit user approval.
Inputs
campaignIdstringrequired- Campaign whose step is skipped (cam_xxx).
stepIdstringrequired- Step to skip for every lead (stp_xxx), from get_campaign_sequences on that campaign.
confirmedboolean- Two-phase safety gate. Omit it (or pass false) on the first call to get how many leads are impacted and where they continue; pass true only after the user has explicitly approved.
lemlistmcp_skip_step_for_leadSkip one step of a campaign sequence for one lead or a list of leads (up to 100), in one call.WriteSkip Step For Lead
Skip one step of a campaign sequence for one lead or a list of leads (up to 100), in one call. Each lead is not stopped: the step never runs for it, it moves on to the following step after that step's own delay, and the skipped step is recorded as "Step skipped" in the review and the reports. Use when the user asks to: - Drop a step for one or several prospects it no longer fits: they replied, accepted the LinkedIn invite, or were reached on another channel - Move one lead, or a set of leads, past the step they are waiting on, without pausing or removing them Two-phase confirmation flow (mandatory): 1. First call WITHOUT confirmed: returns status="awaiting_confirmation" with, for the requested leads, how many will skip (toSkip), how many already had it (alreadySkipped), which ids were not found in this campaign (notFound), and where they continue (destination). A call of 20 leads or more carries "advice": tell the user, offer skip_step_for_everyone, and only call again with confirmed=true if they still want these leads only. The confirm answer repeats it; once the user chose these leads only, go on. 2. Once approved, call again with confirmed=true and the SAME leadIds to skip. Typical flow: get_campaigns -> search_campaign_leads (collect the lead ids) -> get_campaign_sequences (read the step id) -> skip_step_for_lead (preview) -> ask the user -> skip_step_for_lead (confirmed=true). Contract rules reproduced by the backend: - A condition step is never skippable. - Only leads that have NOT run the step are affected; nothing already sent changes. - Idempotent per lead: a lead already skipped answers status="already_skipped" ("skipped" when this call closed a task still waiting on the step); a lead not in the campaign answers "not_found"; neither fails the call. - A skip cannot be undone, here or in the lemlist UI. - Put every lead of the request in ONE call: never call this tool once per lead. For EVERY lead of the campaign, use skip_step_for_everyone. - Never call with confirmed=true without explicit user approval.
Inputs
campaignIdstringrequired- Campaign the leads belong to (cam_xxx). One campaign per call.
leadIdsarrayrequired- Leads to skip the step for (lea_xxx), from search_campaign_leads on this campaign: one lead, or every lead of the request at once. Max 100 per call; split a larger set into several calls.
stepIdstringrequired- Step to skip (stp_xxx), from get_campaign_sequences on that campaign. One step per call.
confirmedboolean- Two-phase safety gate. Omit it (or pass false) on the first call to get the preview; pass true only after the user has explicitly approved.
lemlistmcp_sync_sender_picturePush a user's lemlist profile picture onto the mailboxes lemlist sold them (Google Workspace, Microsoft 365), so recipients see it next to their emails.WriteSync Sender Picture
Push a user's lemlist profile picture onto the mailboxes lemlist sold them (Google Workspace, Microsoft 365), so recipients see it next to their emails. The push is queued; a cron does it. Use when the user asks about: - Setting their picture / avatar on their sending mailboxes - Re-pushing a picture after they changed it in lemlist Contract rules reproduced by the backend: - scope=not_synced (default) queues only mailboxes that are not up to date and never asks for confirmation. - scope=all re-pushes every mailbox, synced ones included; scope=mailbox re-pushes one mailbox (mailboxId dem_xxx, or email). - Mailboxes already queued or syncing are left out; hand-connected mailboxes are not supported. - Refused when the user has no lemlist profile picture: they upload one in Settings > Account first. Two-phase confirmation flow for scope=all, and for scope=mailbox on a mailbox that already reads synced: 1. First call WITHOUT confirmed: nothing is queued; returns status="awaiting_confirmation" with the mailboxes and counts. Ask the user to approve via ask_question. 2. Once the user explicitly approved, call again with the same input plus confirmed=true. Never send confirmed=true without explicit user approval.
Inputs
scopestringrequired- not_synced (default): every mailbox of the user that is not up to date, synced ones are left alone. all: every mailbox of the user, synced ones pushed again. mailbox: one mailbox, pushed again even when already synced.one of
mailboxnot_syncedalldefaultnot_synced confirmedboolean- Set to true only after the user explicitly approved a previous awaiting_confirmation answer.
emailstring- scope=mailbox only: the mailbox address, instead of mailboxId.
mailboxIdstring- scope=mailbox only: the mailbox id (dem_xxx) as returned by list_mailboxes or get_sender_picture_status.
userIdstring- scope=not_synced|all only: lemlist userId (usr_xxx) of a teammate whose mailboxes to sync. Omit to target the caller. Ignored for scope=mailbox, which always pushes the picture of the mailbox own assignee.
lemlistmcp_transfer_campaign_leads_to_listAdd every lead of a campaign to a CRM contact list, in one call.WriteTransfer Campaign Leads To List
Add every lead of a campaign to a CRM contact list, in one call. Each campaign lead is backed by a CRM contact; this resolves those contacts server-side and adds them to the list. You do NOT need to fetch or enumerate lead/contact IDs first. **When to use:** - User wants to move/copy a whole campaign's leads into a contact list (e.g. "add everyone in campaign X to my 'Hot leads' list") - After identifying a campaign and a target list, to populate the list from the campaign **Parameters:** - campaignId: Campaign ID in cam_xxx format (required). Get it from get_campaigns. - listId: Target contact list ID in clt_xxx format (required). Get it from get_contact_lists, or create one with create_contact_list. Never invent or guess a list ID. **IMPORTANT:** - This ADDS (copies) contacts to the list; it does not remove leads from the campaign or move them off any other list. - Contacts already in the list are silently skipped (idempotent, no duplicates). - The target list must be a static contact list; dynamic lists are rejected. - Leads that have no associated contact are skipped. **Example:** { "campaignId": "cam_abc123def456", "listId": "clt_xyz789" }
Inputs
campaignIdstringrequired- Source campaign ID in cam_xxx format. Every lead in this campaign is transferred. Get valid IDs from get_campaigns.
listIdstringrequired- Target contact list ID in clt_xxx format. Must be a static contact list. Get valid IDs from get_contact_lists; never invent or guess a list ID.
lemlistmcp_update_ai_variable_promptEdit the AI-generation prompt of ONE AI variable column on a campaign (icebreaker, opener, contextual opener, etc.).WriteUpdate Ai Variable Prompt
Edit the AI-generation prompt of ONE AI variable column on a campaign (icebreaker, opener, contextual opener, etc.). Replaces the existing prompt entirely with the provided one. Use get_ai_variable_prompts first to read the current prompt and the exact variable names. Keep the {{variable}} placeholders (e.g. {{firstName}}) in the new prompt, and only reference variables or AI columns that already exist on the campaign. Only prompts owned by your team (the per-campaign copy) can be edited. Shared lemlist/system prompts are read-only and will be rejected. Pass tools to change the column's tools (e.g. tools:["enrich-linkedin"] to scrape LinkedIn — the prompt must reference {{linkedinUrl}}). Enabling any tool forces the premium lemlist model and lemlist-credit billing. Pass an empty array ([]) to remove all tools; the column reverts to your team's default model and billing (still charged in lemlist credits if your team has no own AI provider key, otherwise billed via your own API key). Omitting tools leaves the existing tools untouched and only replaces the prompt. Pass autofill to switch the column's auto-generate toggle: true fills this AI variable for every lead added or imported to the campaign afterwards (CSV import, CRM sync, manual add), false turns that off. Omitting autofill leaves the toggle as it is. Use when the user asks to: - Change how an AI column is generated for a campaign - Tweak / rewrite an icebreaker or opener prompt - Turn auto-generation on new/imported leads on or off for an AI column This mutates campaign configuration, so confirm the change with the user before calling.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx format) that owns the AI variable
promptstringrequired- The new AI-generation prompt. Replaces the existing prompt entirely. Keep {{variable}} placeholders for the fields/columns it should reference.
variableNamestringrequired- Name of the AI variable column to edit (e.g. "icebreaker"). The "variables." prefix is optional.
autofillboolean- Switches the column's "auto-generate" toggle: true generates this AI variable for every lead added or imported to the campaign afterwards (CSV import, CRM sync, manual add), false turns that off. Omit to leave the current setting untouched. ⚠️ COSTS CREDITS: with it on, every newly added lead consumes one generation.
toolsarray- Optional AI tools the column may use while generating each value. "enrich-linkedin" scrapes the lead's LinkedIn profile (the prompt MUST reference {{linkedinUrl}} or {{linkedinUrlSalesNav}}, otherwise the tool never fires and the column stays empty); "scrape-website" reads a URL; "web-search" searches the live web. ⚠️ COSTS CREDITS: enabling ANY tool forces the premium lemlist model and switches billing to lemlist credits, charged per lead at generation time (not now). Omit for a plain text column.
lemlistmcp_update_enrichment_waterfallsReorder or toggle providers, rename, or change the countries of one or more enrichment waterfalls in ONE call.WriteUpdate Enrichment Waterfalls
Reorder or toggle providers, rename, or change the countries of one or more enrichment waterfalls in ONE call. Put every waterfall you want to change in 'updates'; never call this once per waterfall. For each entry, 'providers' is the execution order (first tried first); you may send a partial list and every provider left out is appended disabled, so the stored order always covers the whole catalog. 'conditions' replaces the countries of a custom waterfall; a country may belong to only one waterfall per type, and the default waterfall (the catch-all) takes no condition. Editing a default waterfall makes it follow your order instead of lemlist's — use reset_enrichment_waterfalls to hand it back. Run list_enrichment_waterfalls first for the current order, the taken countries and the legal provider ids. Entries are applied independently: 'results' reports the updated waterfall or an error per waterfallId. Team-shared and free (no credits).
Inputs
updatesarrayrequired- One entry per waterfall to change; applied independently.
lemlistmcp_update_leadUpdate standard fields on an existing lead (firstName, lastName, jobTitle, companyName, email, phone, linkedinUrl, picture, timezone, jobDescription, companyDomain).WriteUpdate Lead
Update standard fields on an existing lead (firstName, lastName, jobTitle, companyName, email, phone, linkedinUrl, picture, timezone, jobDescription, companyDomain). Requires leadId + at least one field to update. For custom variables, use update_lead_variables instead.
Inputs
leadIdstringrequired- The lead ID (lea_xxx format)
companyDomainstring- Company website domain (e.g. "acme.com"); a name alone only matches a company that has no domain
companyNamestring- Company name
emailstring- Email address
firstNamestring- First name
jobDescriptionstring- Job description
jobTitlestring- Job title
lastNamestring- Last name
linkedinUrlstring- LinkedIn profile URL (raw strings like linkedin.com/in/foo accepted)
phonestring- Phone number
picturestring- Profile picture URL
timezonestring- Timezone
lemlistmcp_update_lead_variablesSet custom variables on an existing lead (upsert).WriteUpdate Lead Variables
Set custom variables on an existing lead (upsert). Requires leadId + variables (key-value pairs of non-empty strings). Automatically handles both updating existing variables and adding new ones. IMPORTANT: Do NOT pass standard lead fields as variables — the following keys are FORBIDDEN and will be rejected: email, firstName, lastName, picture, phone, linkedinUrl, timezone, jobTitle, jobDescription, companyName, companyDomain. To update these fields, use the update_lead tool instead. Empty string values are rejected — omit the variable instead.
Inputs
leadIdstringrequired- The lead ID (starts with lea_)
variablesobjectrequired- Custom variables as key-value pairs to set (e.g., {"comicFrame": "panel_3", "tone": "friendly"}). Values must be non-empty strings — omit the variable rather than passing an empty string.
lemlistmcp_update_mailboxUpdate a team mailbox.WriteUpdate Mailbox
Update a team mailbox. Currently supports assignment to a specific SDR (lemlist user); the schema is extensible for future updates (settings, status, …). Idempotent — re-applying the same update is safe. Use in Step 5 of the outreach-infra skill once mailboxes have been provisioned, to wire each mailbox to its intended SDR before warmup starts.
Inputs
assignToUserIdstringrequired- lemlist userId (usr_xxx) to assign the mailbox to
mailboxIdstringrequired- Domain mailbox ID (dem_xxx)
lemlistmcp_update_saved_searchRename a saved People Database search and/or replace its filters.WriteUpdate Saved Search
Rename a saved People Database search and/or replace its filters. Use when the user asks about: - Renaming a saved search - Changing the filters of a saved search ("add France to my CTO search", "update that saved search with these filters") Contract rules reproduced by the backend: - Pass name, filters, or both; at least one is required - filters REPLACE the saved ones entirely: send the full new set, not a diff (load_saved_search gives the current one) - mode is only read with filters and defaults to the saved search's mode - name must stay unique within the team; filter rules are the same as save_people_db_search Returns the saved search in its updated state.
Inputs
savedSearchIdstringrequired- The saved search id (lss_xxx), from list_saved_searches or save_people_db_search
filtersarray- Filters replacing the saved ones entirely. Omit to keep the current ones.
modestring- Mode the new filters target. Only read with filters; defaults to the saved mode.one of
peoplecompanies namestring- New name (unique within the team). Omit to keep the current one.
lemlistmcp_update_self_recovery_configUpdate the current team's self-recovery configuration.WriteUpdate Self Recovery Config
Update the current team's self-recovery configuration. Use when the user asks about: - Enabling or disabling self-recovery - Changing which signals are monitored - Authorizing which actions self-recovery may take automatically - Switching the recovery summary email between daily and weekly Contract rules reproduced by the backend: - Every field is optional; only the fields you send are changed. - monitoredSignals and authorizedActions REPLACE the current lists (send the full set, not a delta). - Only fixDns and recover can be authorized. provision, replace and listHygiene are rejected.
Inputs
authorizedActionsarray- Strategies self-recovery may execute automatically. Only fixDns and recover are accepted; provision, replace and listHygiene are rejected. Replaces the current list.
digestFrequencystring- How often team admins get the recovery summary email: daily, or weekly on Mondays. Sent at 8am in the team timezone, only when self-recovery opened a case.one of
dailyweekly enabledboolean- Turn self-recovery on or off for the account.
monitoredSignalsarray- Signals self-recovery watches. Replaces the current list (send the full set, not a delta).
lemlistmcp_update_taskUpdate a single editable field on one OR many tasks (opportunities) on the Tasks / Focus page.WriteUpdate Task
Update a single editable field on one OR many tasks (opportunities) on the Tasks / Focus page. Use when the user asks about: - Reassigning a task to a teammate (or unassigning it) - Changing a task's priority (none/low/medium/high) - Snoozing a task to a later date - Marking a task done or ignored - Marking a task read or unread Pass opportunityId as a single task ID or an array of task IDs. The same field/value is applied to every task; the result is always the aggregate { field, total, succeeded, failed, results[] } — each entry carries updated or an error code, and a bad task fails on its own without failing the others. Contract rules reproduced by the backend: - Exactly ONE of assignedTo / priority / dueDate / status / read must be provided per call (applied to every task). - assignedTo must be a teammate of the current team, or "unassign". - A paused task cannot be marked "done" (use "ignored"); it also cannot be snoozed (resume it first). - dueDate must be a future ISO 8601 date. - status="done" ONLY marks the task complete — it does NOT send the message. For an email/LinkedIn/WhatsApp task where the message should actually be sent to the lead, use send_task (which sends the content AND marks done). Use status="done" here only to close a task without sending (the "mark as done only" action). For a manual campaign step, send_task schedules-and-marks-done (same end state as status="done"), so both paths converge. - To edit a task's subject or body, use update_task_content instead.
Inputs
opportunityIdstringrequired- A single task ID (opp_xxx) or an array of task IDs. The same change is applied to every task; results are returned per task (partial success — a bad task fails on its own without failing the others).
assignedTostring- Reassign the task: a teammate user ID (usr_xxx), or "unassign" to clear the assignee. The user must belong to the current team.
dueDatestring- Snooze the task until this ISO 8601 date/time, formatted "YYYY-MM-DDTHH:mm:ssZ". Must be strictly in the future.
prioritystring- Task priority. "none" clears the priority.one of
nonelowmediumhigh readboolean- Mark the task read (true) or unread (false).
statusstring- Close the task: "done" (completed) or "ignored" (skipped). Paused tasks cannot be marked "done" — use "ignored".one of
doneignored
lemlistmcp_update_task_contentUpdate the message content (subject and/or body) of a message-based task (opportunity).WriteUpdate Task Content
Update the message content (subject and/or body) of a message-based task (opportunity). Use when the user asks about: - Rewriting or tweaking the body of an email, LinkedIn or WhatsApp task - Changing the subject line of an email task Contract rules reproduced by the backend: - Only message-based task types are supported: email, linkedinSend, whatsappMessage. - subject is only allowed on email tasks. - Provide at least one of subject or message; the untouched field is preserved. - Pass the task's type so the body is formatted for its channel (email body kept as HTML, LinkedIn/WhatsApp as plain text). - For campaign-step tasks the edit is stored as a per-lead override (the shared campaign template is untouched); standalone tasks edit their own template.
Inputs
opportunityIdstringrequired- The task (opportunity) ID to update (opp_xxx format).
typestringrequired- The task type — must match the task's actual type (from get_tasks or get_task_content). Used to format the body for its channel: email is HTML (newlines become <br>), LinkedIn and WhatsApp are plain text.one of
emaillinkedinSendwhatsappMessage messagestring- New message body / content (email body, LinkedIn or WhatsApp message).
subjectstring- New email subject. Only valid for email tasks; rejected for other types.
lemlistmcp_update_watch_listUpdate an existing watch list.WriteUpdate Watch List
Update an existing watch list. Use when the user asks about: - Renaming or changing the emoji of a watch list - Adjusting filters of a watch list - Changing how signals are processed (manual, create_opportunity, push_to_campaign) Contract rules reproduced by the backend: - personaId is accepted ONLY on a company watch list with signalProcessingType="push_to_campaign" (any other config is rejected), and only for teams in the personas beta - personaId is optional there: without it the signals are still recorded, they simply source no contact, so nothing reaches the campaign - get a personaId from list_people_database_personas (or create_people_database_persona); never invent one
Inputs
activatebooleanrequired- Toggle the watch list status. false deactivates (pauses) it immediately. true reactivates a paused watch list (triggers billing and external watcher setup). Cannot toggle a draft. Omit to leave the status unchanged.
filtersarrayrequired- Configuration filters as an array of { filterId, in[], out[] } objects
namestringrequired- New watch list name (non-empty)
signalProcessingTypestringrequired- New signal processing typeone of
manualcreate_opportunitypush_to_campaign watchListIdstringrequired- The watch list ID to update (wat_xxx format)
confirmedboolean- Set to true only after the user has explicitly confirmed reactivating this watch list (e.g. via ask_question). Only read when activate=true; never set it on the first call.
emojistring- New emoji
personaIdstring- Persona (pdp_xxx) used to source contacts from the People Database. Used by a company watch list with signalProcessingType = "push_to_campaign" whose signal type does not already ship its own contact: those company signals carry no contact, so the persona is the only way to know who to reach. Optional — without it the signals are still recorded, they simply source no contact, so nothing reaches the campaign. Rejected on any other configuration, including a signal type that delivers its own contact ("newHire", "promotion"), whose signals reach the campaign on their own.
signalOpportunityTemplateobject- Opportunity template payload, required when signalProcessingType = "create_opportunity"
lemlistmcp_update_watch_list_signalsSet the status of one or several watch list signals to "handled" or "ignored".WriteUpdate Watch List Signals
Set the status of one or several watch list signals to "handled" or "ignored". Use when the user asks about: - Marking signals they have acted on as handled (e.g. after reaching out to the leads) - Ignoring / dismissing irrelevant signals to clean up the signals list - Bulk-updating many signals at once Resolve the signal ids with list_watch_list_signals first. Bulk-capable: pass every id in one call (up to 1000). "reason" is optional and only meaningful when status="ignored".
Inputs
signalIdsarrayrequired- Signal ids (wsi_xxx) to update, from list_watch_list_signals. Bulk-capable; up to 1000 ids per call.
statusstringrequired- Target status to set on every signal: "handled" or "ignored".one of
handledignored reasonstring- Optional note explaining the decision. Only meaningful when status="ignored".
lemlistmcp_add_leads_to_campaignAdd one or more leads (max 100) to a campaign.DestructiveAdd Leads To Campaign
Add one or more leads (max 100) to a campaign. Each lead requires at least one identifying field such as email, first name, last name, or company name.
Inputs
campaignIdstringrequired- The campaign ID (cam_xxx format)
leadsarrayrequired- Leads to add to the campaign (1..100). Each lead must have at least one identifying field.
deduplicateboolean- Avoid duplicates across campaigns (default: false). Applies to every lead: an existing lead in ANOTHER campaign is reported as outcome="skippedDuplicateCrossCampaign".
findEmailboolean- ⚠️ COSTS CREDITS: Find email address from other lead data (LinkedIn, name, company)
findPhoneboolean- ⚠️ COSTS CREDITS: Find phone number from other lead data
linkedinEnrichmentboolean- ⚠️ COSTS CREDITS: Enrich each lead with LinkedIn profile data (job title, company info, etc.)
updateStrategystring- Applies to the contact and company a lead matches (an existing lead is skipped, never updated). An empty standard field is dropped by this tool and never clears; only an empty customVariables value can clear a field, under overwrite. How an EXISTING record is updated: overwriteIgnoreEmpty (default, the behavior when absent: incoming values replace stored ones, an empty incoming value never clears), overwrite (same, but an empty string clears the field), fillEmptyOnly (only fills fields the record has empty, stored values and identifiers are kept). A new record always receives every value sent.one of
overwriteoverwriteIgnoreEmptyfillEmptyOnly verifyEmailboolean- ⚠️ COSTS CREDITS: Verify if the email address is valid and deliverable
lemlistmcp_add_sequence_stepAdd a step to an existing campaign sequence.DestructiveAdd Sequence Step
Add a step to an existing campaign sequence. Use only for modifying already-created campaigns — not for initial campaign creation.
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx)
delaynumberrequired- Days to wait after previous step. Must be 0 for "sendToAnotherCampaign" (the lemlist API does not accept a delay for this type, transfer is immediate).
sequenceIdstringrequired- Sequence ID (seq_xxx)
userConfirmedbooleanrequired- Must be true after user confirms
altMessagestring- Depends on the step type. For "linkedinInvite": the PREMIUM invitation note (max 300 chars) attached to the connection request, used when a sender has LinkedIn Premium (a longer alternative to the standard "message" note). For "linkedinSend" and "linkedinVoiceNote": the note sent to leads who are not 1st-degree connections (the out-of-network "Standard accounts" message, 200 chars), used instead of the message itself. Not allowed on any other step type.
altMessagePremiumstring- "linkedinSend" steps only: the PREMIUM variant of the out-of-network note (max 300 chars), sent instead of altMessage when the sending LinkedIn account is Premium or Sales Navigator. Not allowed on any other step type. When recreating a step read from get_campaign_sequences, carry this field over or the premium note is lost.
callStatusFilterarray- Only for aircallDone ("Call status") condition. Call status/disposition KEYS this branch matches (e.g. ["connected-positive","no-answer"]). Keys are team-specific — call get_call_statuses first to get valid keys. Omit to match any completed call.
ccarray- CC email addresses for an email step, applied to every send of this step. Only valid when type is "email".
conditionKeystring- Condition to check (REQUIRED when type is "conditional"). E.g. emailsOpened, linkedinInviteAccepted.one of
hasEmailAddresshasLinkedinUrlhasPhoneNumbercontactListcustomLeadInfohasScoreemailsOpenedemailsClickedemailsUnsubscribedmeetingBookedlinkedinInviteAcceptedlinkedinOpenedaircallDonelinkedinNetworkCheckhasWhatsappAccount customFieldstring- REQUIRED for conditionKey="customLeadInfo". customField is a lead variable or a contact custom-field NAME (the stored name, not its display label), written bare or as "variables.<name>" — the campaign editor writes "variables.". Use "fields.<name>" only for the contact fields that have no lead-variable form, listed by get_condition_field_options (leadStatus, industry, location…). A company field is written "company.<field>", listed under companyFields; "companyName" or "variables.companyName" is stored as "company.name". A field that does not exist for the campaign is rejected, unless the campaign has no lead variables yet, in which case it is accepted and checked when the campaign launches. Call get_condition_field_options first. Use delayType "within" with delay 0.
customNamestring- The step's own name, shown instead of its type label in the app (max 70 characters). Ignored on conditional steps.
customOperatorstring- REQUIRED for conditionKey="customLeadInfo". Comparison operator: "equal" (exact, case-insensitive), "contains", "empty", "notEmpty".one of
equalcontainsemptynotEmpty customValuestring- For conditionKey="customLeadInfo". Compared value; required for operator "equal"/"contains", must be omitted for "empty"/"notEmpty".
delayTypestring- REQUIRED for all conditional steps. "within" = must happen within delay days, "waitUntil" = wait indefinitely. For field-check conditions (hasEmailAddress, hasLinkedinUrl, etc.), use "within" with delay 0.one of
withinwaitUntil emailStatusFilterarray- Only for hasEmailAddress condition. Filter by email verification status. E.g. ["deliverable"] to only match leads with deliverable email. Omit to check if any email exists.
indexnumber- Zero-based position where to insert the step (0 = first). Omit to append at the end. Use to insert warm-up steps before an existing step (e.g. Like + Comment before an Invite).
leadActionstring- "sendToAnotherCampaign" steps only: what happens to the lead in THIS campaign once it has been moved to the target one. "continue" keeps it running the remaining steps, "pause" pauses it, "stop" ends the campaign for it. Omitted leaves the step unset, which behaves as "continue"; the lemlist UI defaults a newly added step to "stop" (shown as "End"). A transfer that fails always pauses the lead, whatever this says.one of
continuepausestop manualboolean- "Mark as manual" toggle for togglable channel steps (email, linkedinSend, linkedinInvite, linkedinVoiceNote, whatsappMessage, sms). When true, the step becomes a per-lead task the user completes by hand instead of an auto-send — pair with `title` (task label) and optional `priority`. For linkedinVoiceNote it stays orthogonal to recordMode (recordMode = how the audio is produced; manual = whether lemlist auto-sends it or the user does it). Rejected on auto-only step types (linkedinVisit, linkedinFollow, api, enrichment, ...) and on linkedinCommentLastPost (always a task: the user approves the drafted comment), unnecessary on natively-manual types (phone, manual).
messagestring- Message content (REQUIRED when type is "email", "linkedinSend", "linkedinInMail", "whatsappMessage", or "sms"; OPTIONAL but recommended for "linkedinInvite"). For emails: the HTML body. For LinkedIn (InMail included)/WhatsApp/SMS/tasks: the message text. For linkedinVoiceNote: with recordMode="ai" this is the spoken script (Liquid); with recordMode="manual" it is only the optional LinkedIn invite-fallback text (NOT a voice script, the user records the audio). For variations (only when the user asks), use spin: {% spin %}{% variation %}Hi{% variation %}Hey{% endspin %} — each variation must be genuinely different wording, never the same text repeated.
methodstring- REQUIRED when type="api". HTTP method (GET or POST) used by the webhook call. Limited to GET/POST to stay iso with the lemlist UI editor.one of
GETPOST prioritynumber- Task priority for a manual step: 0 = Low, 1 = Medium, 2 = High. Omit for None. Only meaningful when the step will produce a task (either manual:true on a togglable channel step, or a natively-manual type: phone, manual).one of
012 recordModestring- linkedinVoiceNote only: "ai" generates audio from the `message` script via ElevenLabs (message supports Liquid syntax: {{firstName}}, {% if %}, spin, any custom variable); "manual" (default): the user records the audio themselves (a task is created), so do NOT write a spoken script and do NOT set a voiceId.one of
manualai scoreComparatorstring- Only for hasScore condition. "$gte" = score is above or equal to threshold, "$lt" = score is below threshold. Defaults to "$gte".one of
$gte$lt scoreThresholdnumber- Only for hasScore condition. Score threshold value (0-100). Defaults to 80.
subjectstring- Subject line. Email: required for the first email; omit for follow-ups to send as reply thread. linkedinInMail: always required, plain text. Supports Liquid syntax: {{ firstName }}, {% if condition %}...{% endif %}, and spin for variations (only when the user asks): {% spin %}{% variation %}Hello{% variation %}Hi{% endspin %}. Each variation must be genuinely different wording — never repeat the same text.
targetCampaignIdstring- REQUIRED when type is "sendToAnotherCampaign". Target campaign ID (cam_xxx) the lead is moved to. Must belong to the same team as the parent campaign and not be archived. Avoid cycles (A -> B -> A).
titlestring- Task title displayed in the review UI. Set it whenever the step becomes a task: for natively-manual types (manual, phone) or for a togglable channel step marked with manual:true (email, linkedinSend, linkedinInvite, linkedinVoiceNote, whatsappMessage, sms). Required for type="manual".
typestring- Type of step. Use "conditional" for branching based on lead behavior, "sendToAnotherCampaign" to move a lead to another campaign, "api" to fire an outbound HTTP webhook (provide method and url), "enrichment" to automatically enrich leads at this step (provide workflowIds; costs credits; requires MULTI plan).one of
emaillinkedinSendlinkedinInMaillinkedinLikeLastPostlinkedinEndorselinkedinInvitelinkedinFollowlinkedinVisitlinkedinCommentLastPostlinkedinWithdrawInvitationlinkedinVoiceNotemanualphoneconditionalsendToAnotherCampaignwhatsappMessagesmsapienrichmentdefaultemail urlstring- REQUIRED when type="api". Webhook URL fired during sequence execution. Supports Liquid templating against lead variables (e.g. https://example.com/leads/{{ _id }}). For POST, the request body is automatically the lead data; custom bodies and custom headers are not supported.
voiceIdstring- linkedinVoiceNote + recordMode="ai" only: ElevenLabs voice profile id used to speak the script. Call list_voice_profiles first to get valid ids (lemlist default voices + your team cloned voices). Must belong to your team.
workflowIdsarray- Required when type is "enrichment". One or more enrichment workflows: "find_email_01" (find email), "find_phone_01" (find phone), "verify_01" (verify email deliverability), "linkedin_01" (full LinkedIn profile). Each workflow costs credits.
lemlistmcp_add_unsubscribeAdd email to unsubscribe blocklist.DestructiveAdd Unsubscribe
Add email to unsubscribe blocklist. Blocks all future campaign sends. Use delete_unsubscribe to reverse.
Inputs
valuesarrayrequired- Values to block: email addresses, email domains starting with @ (e.g. @acme.com) to block every address on that domain, phone numbers or LinkedIn URLs (the lead's linkedinUrl, copied verbatim). Pass every target the user asked to unsubscribe in one call: do not split into multiple calls.
lemlistmcp_create_campaign_with_sequenceCreate campaign.DestructiveCreate Campaign With Sequence
Create campaign. If subject AND body are provided, creates the first email step. If omitted, creates an empty sequence (use add_sequence_step to add a condition or any step type as the first step). Call add_sequence_step for each additional step. Supports Liquid syntax.
Inputs
namestringrequired- The name of the campaign
bodystring- Email body content for the first email (supports Liquid syntax). Omit both subject and body to create campaign without a first step.
emojistring- Optional emoji for the campaign (e.g., 🚀, 💎, 🔥)
subjectstring- Subject line for the first email (supports Liquid syntax). Omit both subject and body to create campaign without a first step (e.g. when first step is a condition).
timezonestring- Timezone for the campaign (e.g., Europe/Paris, America/New_York)default
Europe/Paris
lemlistmcp_create_webhookCreate a webhook for real-time campaign activity notifications.DestructiveCreate Webhook
Create a webhook for real-time campaign activity notifications. Max 200 per account, no duplicate URLs. Filter by activity type (emailsSent, emailsOpened, emailsReplied, etc.) and/or campaignId.
Inputs
targetUrlstringrequired- The webhook URL to call (must start with http:// or https://)
campaignIdstring- Optional: Filter to call webhook only for specific campaign (e.g., "cam_xxx")
isFirstboolean- Optional: Call webhook only for first activity of this type
typestring- Optional: Filter to call webhook only for specific activity types (e.g., "emailsReplied", "emailsSent")
zapIdstring- Optional: Zapier integration ID
lemlistmcp_delete_campaign_folderDelete one or more campaign folders.DestructiveDelete Campaign Folder
Delete one or more campaign folders. Campaigns are NEVER deleted — each folder's campaigns and sub-folders move up one level (to the parent folder, or to the root if the folder was at the top). Use when the user asks about: - Removing one or several folders while keeping their campaigns (pass them all in `folderIds`). Contract rules reproduced by the backend: - Deleting a folder is blocked if a sub-folder would collide, by name, with a folder already present in the parent — rename the conflicting sub-folder first. - Best-effort: each id is handled independently; the response lists `deleted` and `failed` (with a per-id reason).
Inputs
folderIdsarrayrequired- Folder IDs (cfo_xxx) to delete (1-100). Pass every folder in one call rather than deleting one at a time.
lemlistmcp_delete_campaign_leadsPermanently delete leads from ONE campaign in a single bulk call.DestructiveDelete Campaign Leads
Permanently delete leads from ONE campaign in a single bulk call. Irreversible: it also removes each lead's activity and task history in that campaign. Use when the user asks about: - Removing a specific set of leads from a campaign at once (e.g. after moving or segmenting them elsewhere) - Cleaning leads out of a campaign without calling the unitary delete endpoint one lead at a time Two-phase confirmation flow (mandatory): 1. First call WITHOUT confirmed (or confirmed=false): returns the count and a sample of the exact leads that will be deleted, with status="awaiting_confirmation". Show these to the user and get explicit approval via ask_question. 2. Once the user has approved, call again with confirmed=true to actually delete. Contract rules reproduced by the backend: - Scoped to a SINGLE campaignId; a lead id is unique to one campaign (campaignId + email). - Every leadId MUST belong to campaignId. Ids from another campaign (or already deleted) are rejected with an error, not silently skipped. Re-fetch the ids via search_campaign_leads on the target campaign. - At most 1000 lead ids per call. - Never call with confirmed=true without explicit user approval.
Inputs
campaignIdstringrequired- The campaign (cam_xxx) to remove leads from. A single campaign only, never a list of campaigns.
leadIdsarrayrequired- Lead ids (lea_xxx) to permanently delete from this campaign. Every id MUST belong to campaignId (a lead id is unique to one campaign). Max 1000 per call. Get the ids from search_campaign_leads run on this same campaign.
confirmedboolean- Two-phase safety gate. Omit it (or pass false) on the first call to preview exactly what will be deleted; only pass true after the user has explicitly approved the deletion.
lemlistmcp_delete_companyPermanently delete a company record from Lemlist.DestructiveDelete Company
Permanently delete a company record from Lemlist. Only removes the Lemlist record — does not affect any connected CRM.
Inputs
companyIdstringrequired- Lemlist company ID to delete (cpn_xxx).
userConfirmedbooleanrequired- REQUIRED: Must be true. Confirms user explicitly approved this deletion. Only set to true after user says yes.
forceboolean- When true, detaches any attached contacts (unsets their companyId) before deleting the company.
lemlistmcp_delete_contactREQUIRES CONFIRMATION: Delete a lemlist contact by its ID (ctc_xxx) or email.DestructiveDelete Contact
REQUIRES CONFIRMATION: Delete a lemlist contact by its ID (ctc_xxx) or email. Only the lemlist record is removed — no CRM-side propagation. Deletion cascades to the contact's leads, opportunities, list memberships, inbox conversations and activities. REQUIRES userConfirmed=true. Fails with CONTACT_DELETE_BLOCKED when the contact cannot be deleted right now (e.g. an enrichment is still running — retry once it finishes).
Inputs
idOrEmailstringrequired- Lemlist contact ID (ctc_xxx) or the contact email address to delete.
userConfirmedbooleanrequired- REQUIRED: Must be true. Confirms user explicitly approved this deletion. Only set to true after user says yes.
lemlistmcp_delete_enrichment_waterfallsDelete one or more custom enrichment waterfalls in ONE call; their countries fall back to the default waterfall of the type.DestructiveDelete Enrichment Waterfalls
Delete one or more custom enrichment waterfalls in ONE call; their countries fall back to the default waterfall of the type. Default waterfalls (default_email, default_phone) cannot be deleted — use reset_enrichment_waterfalls. Ids are applied independently: 'results' reports 'deleted: true' or an error per waterfallId. Team-shared and free (no credits).
Inputs
waterfallIdsarrayrequired- Custom waterfall ids to delete; default ids are refused (reset them instead).
lemlistmcp_delete_memoryDelete a stored memory entry by topic so it is no longer recalled in future conversations.DestructiveDelete Memory
Delete a stored memory entry by topic so it is no longer recalled in future conversations.
Inputs
topicstringrequired- The topic key to delete (e.g. "preferred_tone")
scopestring- Scope: "user" (default) or "team"one of
userteam
lemlistmcp_delete_people_database_personaDelete a People Database persona of the current team.DestructiveDelete People Database Persona
Delete a People Database persona of the current team. The deletion is permanent. Use when the user asks about: - Removing a persona they no longer need - Cleaning up duplicate or outdated personas Important: - Ask the user to confirm before calling this tool; there is no undo. - Use list_people_database_personas to resolve a persona name into its id. - An unknown id, another team's persona, and the auto-generated system persona all fail the same way (not found) — none of them can be deleted here.
Inputs
personaIdstringrequired- The persona id to delete (pdp_xxx format)
lemlistmcp_delete_saved_searchDelete a saved People Database search of the current team.DestructiveDelete Saved Search
Delete a saved People Database search of the current team. The deletion is permanent. Use when the user asks about: - Removing a saved search they no longer need - Cleaning up duplicate or outdated saved searches Important: - Ask the user to confirm before calling this tool; there is no undo. - Use list_saved_searches to resolve a saved search name into its id. - An unknown id and another team's saved search both fail as not found.
Inputs
savedSearchIdstringrequired- The saved search id (lss_xxx), from list_saved_searches or save_people_db_search
lemlistmcp_delete_sequence_stepDelete a step from a campaign sequence.DestructiveDelete Sequence Step
Delete a step from a campaign sequence. Use only when removing a step added by mistake — requires user confirmation.
Inputs
campaignIdstringrequired- The campaign ID (required for status verification)
sequenceIdstringrequired- The sequence ID (starts with seq_)
stepIdstringrequired- The step ID to delete (starts with stp_)
userConfirmedbooleanrequired- REQUIRED: Must be true. Confirms user explicitly approved this deletion. Only set to true after user says yes.
lemlistmcp_delete_unsubscribeRemove an email address from the unsubscribe list, allowing it to be contacted again in future campaigns.DestructiveDelete Unsubscribe
Remove an email address from the unsubscribe list, allowing it to be contacted again in future campaigns.
Inputs
valuesarrayrequired- Values to re-subscribe: email addresses, email domains starting with @ (e.g. @acme.com) to unblock every address on that domain, phone numbers or LinkedIn URLs, each exactly as it was blocked. Pass every target the user asked to re-subscribe in one call: do not split into multiple calls.
confirmedboolean- NEVER set this to true on first call. Only set to true after the user has explicitly confirmed the action (e.g. via ask_question).
lemlistmcp_delete_watch_listDelete a watch list and immediately stop processing signals for it.DestructiveDelete Watch List
Delete a watch list and immediately stop processing signals for it.
Inputs
watchListIdstringrequired- The watch list ID to delete (wat_xxx format)
confirmedboolean- Set to true only after the user has explicitly confirmed the deletion (e.g. via ask_question).
lemlistmcp_delete_webhookDelete a webhook from your Lemlist account, stopping all notifications to that endpoint immediately.DestructiveDelete Webhook
Delete a webhook from your Lemlist account, stopping all notifications to that endpoint immediately.
Inputs
webhookIdstringrequired- The webhook ID to delete (starts with hoo_)
confirmedboolean- Set to true only after the user has explicitly confirmed the deletion (e.g. via ask_question).
lemlistmcp_disconnect_email_accountDisconnect email account.DestructiveDisconnect Email Account
Disconnect email account. Stops sending immediately. Cannot be undone. Use get_user_channels to find account ID.
Inputs
emailAccountIdstringrequired- The email account ID to disconnect (starts with usm_)
confirmedboolean- Set to true only after the user has explicitly confirmed the disconnection (e.g. via ask_question).
lemlistmcp_import_companies_from_csvImport companies into a company list from an uploaded CSV.DestructiveImport Companies From Csv
Import companies into a company list from an uploaded CSV. Use when the user asks about: - Loading a CSV/spreadsheet of companies or accounts into a company list - Bulk-adding companies to the CRM Company columns are mapped to their bare keys here (name, domain, industry, …) — the company-prefixed form belongs to a contact CSV. Flow (3 steps, in order): 1. create_upload_url → get an uploadUrl + uploadKey 2. PUT the raw CSV to uploadUrl 3. this tool with the uploadKey Contract rules reproduced by the backend: - the list must exist, hold this entity type, and not be dynamic (filter-driven lists are filled by their own sync) - uploadKey must come from create_upload_url; a made-up key is rejected - at least one column must be mapped, and no mapping may target a reserved key (_id, teamId, …) - a "customField" target only lands if that custom field already exists on the team - every imported entity — created, updated or unchanged — is added to the list A target that matches no field is re-resolved server-side from the header and a sample value (so an export header like companyWebsite still fills companyDomain). Whatever it corrects comes back as repairedColumns; anything it cannot place comes back as ignoredColumns — those columns were NOT imported, so check them before reporting success.
Inputs
columnMappingobjectrequired- Maps each CSV header to the field it fills: { "CSV header": "fieldKey" }. Valid fieldKeys come from get_contact_fields_schema (csvImportTargets). Use "customField" to store a column in the custom field named after that header. Columns left out of the map are not imported.
listIdstringrequired- Target list ID (clt_xxx format). Get it from get_company_lists.
uploadKeystringrequired- uploadKey returned by create_upload_url. The CSV must already be uploaded.
updateStrategystring- How a MATCHED entity is updated: "overwriteIgnoreEmpty" (default: incoming values replace stored ones, an empty cell never clears), "overwrite" (an empty cell clears the field), "fillEmptyOnly" (only fills empty fields, stored values and identifiers are kept).one of
overwriteoverwriteIgnoreEmptyfillEmptyOnly upsertModestring- How existing entities are handled: "createAndUpdate" (default) creates new ones and updates matches, "createOnly" skips matches, "updateOnly" only updates and creates nothing.one of
createOnlyupdateOnlycreateAndUpdate
lemlistmcp_import_contacts_from_csvImport contacts into a contact list from an uploaded CSV.DestructiveImport Contacts From Csv
Import contacts into a contact list from an uploaded CSV. Use when the user asks about: - Loading a CSV/spreadsheet of people into a contact list - Bulk-adding contacts to the CRM without starting a campaign (To put leads into a campaign instead, use import_leads_to_campaign.) A contact CSV can also carry the company columns: map them to the company-prefixed keys (companyName, companyDomain, …) and the company is created and linked in the same pass. Flow (3 steps, in order): 1. create_upload_url → get an uploadUrl + uploadKey 2. PUT the raw CSV to uploadUrl 3. this tool with the uploadKey Contract rules reproduced by the backend: - the list must exist, hold this entity type, and not be dynamic (filter-driven lists are filled by their own sync) - uploadKey must come from create_upload_url; a made-up key is rejected - at least one column must be mapped, and no mapping may target a reserved key (_id, teamId, …) - a "customField" target only lands if that custom field already exists on the team - every imported entity — created, updated or unchanged — is added to the list A target that matches no field is re-resolved server-side from the header and a sample value (so an export header like companyWebsite still fills companyDomain). Whatever it corrects comes back as repairedColumns; anything it cannot place comes back as ignoredColumns — those columns were NOT imported, so check them before reporting success.
Inputs
columnMappingobjectrequired- Maps each CSV header to the field it fills: { "CSV header": "fieldKey" }. Valid fieldKeys come from get_contact_fields_schema (csvImportTargets). Use "customField" to store a column in the custom field named after that header. Columns left out of the map are not imported.
listIdstringrequired- Target list ID (clt_xxx format). Get it from get_contact_lists.
uploadKeystringrequired- uploadKey returned by create_upload_url. The CSV must already be uploaded.
updateStrategystring- How a MATCHED entity is updated: "overwriteIgnoreEmpty" (default: incoming values replace stored ones, an empty cell never clears), "overwrite" (an empty cell clears the field), "fillEmptyOnly" (only fills empty fields, stored values and identifiers are kept).one of
overwriteoverwriteIgnoreEmptyfillEmptyOnly upsertModestring- How existing entities are handled: "createAndUpdate" (default) creates new ones and updates matches, "createOnly" skips matches, "updateOnly" only updates and creates nothing.one of
createOnlyupdateOnlycreateAndUpdate
lemlistmcp_import_leads_to_campaignImport leads into a campaign from a CSV uploaded with create_upload_url — the fast path for files of hundreds/thousands of leads (no lead data passes through the conversation).DestructiveImport Leads To Campaign
Import leads into a campaign from a CSV uploaded with create_upload_url — the fast path for files of hundreds/thousands of leads (no lead data passes through the conversation). Flow: 1. create_upload_url({ purpose: "leadsCsv", fileName, fileSize }) → uploadUrl + uploadKey 2. PUT the file to uploadUrl (curl -T) 3. import_leads_to_campaign({ campaignId, uploadKey, columnMapping }) columnMapping maps each CSV header to a lead field (read the file's header row to build it). The server downloads, parses, dedups, optionally enriches, and bulk-inserts in one pass, then returns compact counters ({ total, added, updated, skipped, failed }) plus a capped list of failure rows — NOT the full list of imported leads. ⚠️ Credit cost, charged only on success — a lookup that returns nothing costs 0: findEmail 5 per email found, linkedinEnrichment 1 per profile enriched, findPhone 20 per phone number found, verifyEmail 1 per email verified. Multiply by the number of imported rows to quote a maximum before enabling them.
Inputs
campaignIdstringrequired- The campaign ID (starts with cam_)
columnMappingobjectrequired- CSV header → target lead field, e.g. {"Email pro":"email","LinkedIn":"linkedinUrl"}. Targets: built-in fields, any custom variable name, or "customField". Unmapped columns are ignored. ownerId / companyOwnerId set the contact's / its company's owner from a team member email or usr_ id.
uploadKeystringrequired- Storage key returned by create_upload_url (purpose="leadsCsv").
deduplicateboolean- Avoid duplicates across campaigns (default: false).
findEmailboolean- COSTS CREDITS: Find email address from other lead data.
findPhoneboolean- COSTS CREDITS: Find phone number from other lead data.
linkedinEnrichmentboolean- COSTS CREDITS: Enrich each lead with LinkedIn profile data.
maxRowsnumber- Max rows to import (default 10000, capped at 40000).
updateStrategystring- How an EXISTING record is updated: overwriteIgnoreEmpty (default, the behavior when absent: incoming values replace stored ones, an empty incoming value never clears), overwrite (same, but an empty string clears the field), fillEmptyOnly (only fills fields the record has empty, stored values and identifiers are kept). A new record always receives every value sent.one of
overwriteoverwriteIgnoreEmptyfillEmptyOnly verifyEmailboolean- COSTS CREDITS: Verify the email address is valid.
lemlistmcp_provision_mailboxesCreate mailbox orders on a team-owned domain.DestructiveProvision Mailboxes
Create mailbox orders on a team-owned domain. Call this IMMEDIATELY after purchase_domain for each domain — do not wait for the domain to become Active first. The mailbox type is locked by the domain's provider (set at purchase and irreversible): - "google" domain → Google Workspace mailboxes only - "m365" domain → Microsoft 365 mailboxes only - "infraforge" domain → SMTP mailboxes only Both domain and mailbox orders are processed asynchronously by background cron jobs (Ordered → Paid → Registered → Pending → Active). This can take minutes to hours (M365: up to 2 days). After calling this tool, tell the user to wait until their domains show as Active before proceeding with DNS configuration. **DESTRUCTIVE — IRREVERSIBLE CHARGE.** Creates real mailbox orders and bills the team. Only call after the user has explicitly confirmed the full infrastructure plan. Returns `{ added, mailboxes: [{ _id, email, assignToUser }] }` — keep the `_id` (`dem_xxx`) of each created mailbox if any later `update_mailbox` call is anticipated (e.g. recovery, re-assignment).
Inputs
domainIdstringrequired- Domain ID (dom_xxx)
emailsarrayrequired- Mailboxes to create
lemlistmcp_purchase_domainPurchase (or transfer) a domain at the registrar.DestructivePurchase Domain
Purchase (or transfer) a domain at the registrar. Charges the team via Stripe. **DESTRUCTIVE — IRREVERSIBLE CHARGE.** Only call after the user has explicitly confirmed the full infrastructure plan (Step 7 of the outreach-infra skill). The plan must include price, provider, and any pre-set DNS records. **CRITICAL: The provider locks the mailbox type for this domain permanently.** A domain purchased with "google" can ONLY have Google Workspace mailboxes. A domain purchased with "m365" can ONLY have Microsoft 365 mailboxes. A domain purchased with "infraforge" can ONLY have SMTP mailboxes. This choice is irreversible — you cannot change the provider after purchase. Choose carefully based on the ESP split from calculate_infrastructure. **Registrant contact:** the registrar requires a billing/registrant contact. On the team's FIRST purchase, pass `billingContact` (collected from the user). It is stored on the team and reused for later domains, so omit it once set. Purchasing without a contact (and none stored) fails with a clear error.
Inputs
domainstringrequired- Domain to purchase
providerstringrequired- Locks the mailbox type permanently: "google" = Google Workspace only, "m365" = Microsoft 365 only, "infraforge" = SMTP only. Irreversible.one of
infraforgegooglem365 authCodestring- Transfer auth code (if isTransfer)
billingContactobject- Domain registrant/billing contact. Required on the team's first purchase (the registrar needs it); stored and reused afterwards, so omit it once set. Providing it overwrites the existing contact.
dnsRecordsarray- Optional initial DNS records
isTransferboolean- Transfer an existing domain instead of registering
lemlistmcp_remove_campaign_sendersRemove one or more senders from the sender pool of a campaign.DestructiveRemove Campaign Senders
Remove one or more senders from the sender pool of a campaign. Every channel of the sender leaves with it. The leads not contacted yet go to the remaining senders; the contacted leads keep their sender. Keywords: remove a sender, take Carl off the campaign, stop sending from this user. Use when the user asks about: - Removing a teammate from a campaign ("remove Carl from the Q4 campaign") - Removing a teammate who left the team or lost a seat To remove one channel and keep the person, use disable_campaign_sender_channels instead. Call get_campaign_senders first: it gives the user ids of the pool. A user outside the pool changes nothing and raises no error. Two-phase confirmation flow: 1. First call WITHOUT confirmed: the backend counts the contacted leads that keep a sender or a channel this change removes (impact.contactedLeads). Above 0 the call writes nothing and answers status="awaiting_confirmation" with "data" (the pool after the change) and the count: show them to the user, ask with ask_question, then call again with confirmed=true and the SAME input. At 0 the call writes at once. 2. Never call with confirmed=true without explicit user approval. Contract rules reproduced by the backend: - A campaign with a dynamic sender strategy (contactOwner, leadImporter, customField:<field>) has a read-only pool that the leads fill: every pool write is refused. Switch the strategy with set_campaign_sender_strategy first, only when the user wants it. - A channel type is accepted only when a non-forced step of the campaign uses it: no WhatsApp channel without a WhatsApp step. Add the step first with add_sequence_step: it enables the type on each pool sender who owns an account of it, and lists the enabled types in senderPoolChange. - Every entry must be a team member who owns the listed channels; an alias must be one of the aliases of the mailbox. - LinkedIn has no channel to pick: a sender in the pool is enabled on LinkedIn. Never list linkedin with an id. - A pool with more than one sender needs the emailPro plan; the mailboxes above the plan allowance are dropped. - A write runs on a running campaign: the leads not contacted yet get a new sender from the pool, the contacted leads keep theirs. - Every write answers "data": the pool after the change, with the diagnostics of each sender and channel. Read them and report the errors to the user.
Inputs
campaignIdstringrequired- The campaign (cam_xxx), from get_campaigns.
userIdsarrayrequired- The senders to remove (usr_xxx), from get_campaign_senders. Put every sender of the request in ONE call: one write, one task sender sync, one confirmation.
confirmedboolean- Two-phase safety gate. Omit it (or pass false) on the first call to get the preview; pass true only after the user has explicitly approved.
lemlistmcp_remove_companies_from_listRemove existing CRM companies from a company list.DestructiveRemove Companies From List
Remove existing CRM companies from a company list. Use when the user asks about: - Taking companies out of a list (clean up duplicates, drop a wrong segment) - Removing a subset of a list found with search_companies and its listId filter Contract rules reproduced by the backend: - this removes list MEMBERSHIP only; the companies themselves are never deleted - the list id comes from get_company_lists — never invent one - the list must exist, hold companies (not contacts), and not be dynamic - companies that are not in the list are silently skipped, not an error - at most 1000 companies per call
Inputs
companyIdsarrayrequired- Company IDs in cpn_xxx format (max 1000). Get them from search_companies.
listIdstringrequired- Company list ID (clt_xxx format). Get it from get_company_lists.
lemlistmcp_remove_contacts_from_listRemove existing CRM contacts from a contact list.DestructiveRemove Contacts From List
Remove existing CRM contacts from a contact list. **When to use:** - User wants to take contacts out of a list (e.g. clean up duplicates, drop a wrong segment) - After searching a list with search_contacts (listId filter), user wants to remove a subset **Parameters:** - contactIds: Contact IDs in ctc_xxx format (required, max 1,000) - listId: Contact list ID in clt_xxx format to remove them from (required) **IMPORTANT:** - This removes list membership ONLY. It does NOT delete the contacts themselves. - To get the contacts currently in a list, call search_contacts with that listId, then keep the ones to remove (e.g. those without a LinkedIn URL) and pass their ids here. - To get a valid listId, call get_contact_lists first — never invent or guess a list ID. - Contacts that are not in the list are silently skipped (no error). - The list must be a static contact list; you cannot manually remove from a dynamic list. **Example:** { "contactIds": ["ctc_abc123", "ctc_def456"], "listId": "clt_xyz789" }
Inputs
contactIdsarrayrequired- Contact IDs in ctc_xxx format (max 1000). Get them from search_contacts.
listIdstringrequired- Contact list ID (clt_xxx format). Get it from get_contact_lists.
lemlistmcp_remove_watch_list_trigger_from_campaignDetach a watch list signal trigger from a campaign: its future signals stop feeding the campaign.DestructiveRemove Watch List Trigger From Campaign
Detach a watch list signal trigger from a campaign: its future signals stop feeding the campaign. Keywords: remove trigger, detach agent, stop feeding campaign, unlink watch list. Use when the user asks about: - Removing a trigger / an agent from a campaign - Stopping a watch list from pushing leads into a campaign Contract rules reproduced by the backend: - Leads already pushed stay in the campaign; the watch list itself is kept (delete_watch_list removes it) - The watch list goes back to signalProcessingType="manual" only when no other campaign still uses it - The campaign keeps its signal-based mode and its import options, so re-linking restores the previous setup - No user confirmation required
Inputs
campaignIdstringrequired- Campaign ID (cam_xxx) to detach the trigger from.
watchListIdstringrequired- Watch list ID (wat_xxx) currently linked as a trigger.
lemlistmcp_report_unsupported_caseReport a feature request or unsupported use case to the product team.DestructiveReport Unsupported Case
Report a feature request or unsupported use case to the product team. Use this ONLY when the user's request is something lemlist should support but the copilot cannot do yet, AND the user has agreed to have their feedback reported. Do NOT use for off-topic requests unrelated to lemlist. Always provide inputs in English, translate if needed.
Inputs
descriptionstringrequired- What the user asked for that is not supported yet (MUST be in English). A product reader has to get it in seconds: open with ONE sentence naming the missing capability, which becomes the case title and has to stand on its own, then add 2 to 4 lines each starting with the "•" character, one idea per line, for what the user was trying to do, what blocks them today, and the workaround they were left with if there is one. Never restate the opening sentence.
conversationContextstring- Brief summary of the conversation context leading to this request (MUST be in English)
lemlistmcp_save_business_contextSave the user business context for future conversations.DestructiveSave Business Context
Save the user business context for future conversations. Use this after collecting company information from the user to remember it across conversations.
Inputs
markdownstringrequired- The COMPLETE business context document as markdown, which replaces what is stored. Call get_business_context first and edit from its current content: preserve existing information unless the user asked to remove it, integrate new facts into the relevant existing section instead of appending a duplicate one, keep the heading structure, and return the whole document (never a fragment). Plain markdown only, no HTML comments.
lemlistmcp_send_messageSend a message to a contact or lead via email, LinkedIn, WhatsApp, or SMS from the Lemlist inbox.DestructiveSend Message
Send a message to a contact or lead via email, LinkedIn, WhatsApp, or SMS from the Lemlist inbox.
Inputs
channelstringrequired- Channel to send through.one of
emaillinkedinsmswhatsapp messagestringrequired- Message content (channel-specific format).
sendUserIdstringrequired- User ID sending the message (usr_xxx format).
ccarray- channel="email": CC addresses. On a reply, omit to inherit the thread's CC, pass a list to override, or [] to send without CC.
contactIdstring- Contact ID to send to. Required if leadId is not provided.
fromstring- channel="sms": sender phone number.
leadIdstring- Lead ID to send to. Alternative to contactId.
replyToActivityIdstring- channel="email": reply within a thread — a specific email activity id (act_xxx) or "latest" for the contact's most recent email. Threads the message and reuses the thread's subject.
sendUserEmailstring- channel="email": email address to send from.
sendUserMailboxIdstring- channel="email": mailbox ID (usm_xxx) to use for sending.
sendUserWhatsappAccountIdstring- channel="whatsapp": WhatsApp account ID (uwa_xxx) to use for sending.
subjectstring- channel="email": email subject line (overridden by the thread subject on a reply).
lemlistmcp_send_taskSend one OR many message-based tasks now, OR — for a manual campaign step — schedule it via the campaign and mark it done (the "Send & mark done" / "Schedule & mark done" actions on the Tasks / Focus page).DestructiveSend Task
Send one OR many message-based tasks now, OR — for a manual campaign step — schedule it via the campaign and mark it done (the "Send & mark done" / "Schedule & mark done" actions on the Tasks / Focus page). Use when the user asks to actually SEND an email, LinkedIn message or WhatsApp message task (not merely close it), or to schedule/complete a manual campaign step. Pass opportunityId as a single task ID or an array of task IDs. The same sender is applied to every task; the result is always the aggregate { total, succeeded, failed, results[] } — each entry carries channel/sent/scheduled or an error code, and a bad task fails on its own without failing the others. With an array this contacts MULTIPLE leads at once — only call it after the user has approved sending to all of them. What it sends: - The task's current content: the per-lead draft override if one exists, otherwise the task's template. Edit it first with update_task_content if needed. Contract rules reproduced by the backend: - Regular (non-manual) email, linkedinSend and whatsappMessage tasks are sent immediately; this requires the per-channel sender fields, a contact and non-empty content (email needs a subject). Returns sent=true, scheduled=false. - A manual campaign step is NOT sent immediately: it is handed back to the campaign to send at its scheduled time and marked done. Sender fields, contact and content are NOT required (the campaign step's own content is used at send time). Returns sent=false, scheduled=true. - Still only email, linkedinSend and whatsappMessage types are handled; phone, SMS, generic manual and other types are rejected — close those with update_task status="done". - The task must be open (due/upcoming); paused or already-closed tasks are rejected. - Sender details for a direct send: email needs sendUserEmail + sendUserMailboxId; whatsapp needs sendUserWhatsappAccountId; linkedin needs only sendUserId. Discover valid sender ids/mailboxes with get_users / get_user_channels. - This actually contacts the lead (now, or at the scheduled time) — only call it after the user has approved sending.
Inputs
opportunityIdstringrequired- A single task ID (opp_xxx) or an array of task IDs. The same change is applied to every task; results are returned per task (partial success — a bad task fails on its own without failing the others).
sendUserIdstringrequired- User ID (usr_xxx) sending the message. Required for every channel. Ignored for a manual campaign step (scheduling uses the campaign step's own sender).
ccarray- CC email addresses (email tasks only).
sendUserEmailstring- Sending email address. REQUIRED for email tasks; ignored otherwise.
sendUserMailboxIdstring- Mailbox ID (usm_xxx) to send from. REQUIRED for email tasks; ignored otherwise.
sendUserWhatsappAccountIdstring- WhatsApp account ID (uwa_xxx). REQUIRED for WhatsApp tasks; ignored otherwise.
lemlistmcp_set_campaign_stateStart, pause, archive, or unarchive a campaign to change its running state.DestructiveSet Campaign State
Start, pause, archive, or unarchive a campaign to change its running state.
Inputs
actionstringrequired- "start" to resume a paused campaign, "pause" to pause a running campaign, "archive" to archive a campaign, "unarchive" to restore an archived campaign.one of
startpausearchiveunarchive campaignIdstringrequired- The campaign ID (cam_xxx format).
userConfirmedboolean- REQUIRED for action="start" and action="archive": must be true after the user explicitly confirms. Not required for pause or unarchive.
lemlistmcp_update_call_statusSet the disposition (call status) on a single dialer call activity.DestructiveUpdate Call Status
Set the disposition (call status) on a single dialer call activity. Use when the user asks to: - Log or correct the outcome of a call (e.g. after reading its transcript) - Back-fill a missing status on a call that was dialed but never dispositioned Typical flow: get_call_activities (find calls, spot empty statuses) → get_call_details (read the transcript) → get_call_statuses (get the valid keys) → update_call_status (set the key). Setting a status is NOT cosmetic: depending on how the team configured that status, it can mark the lead interested / not-interested and pause campaigns, and it syncs to the connected CRM — exactly like a rep clicking it in the UI.
Inputs
activityIdstringrequired- The call activity ID (act_xxx) — from get_call_activities or get_call_details
callStatusstringrequired- The call status KEY to set (e.g. "left-voicemail", "connected-positive"). Must be one of the team's configured status keys — call get_call_statuses first to get them. An invalid key returns the list of valid keys so you can retry.
lemlistmcp_update_sequence_stepUpdate a step in an existing campaign sequence.DestructiveUpdate Sequence Step
Update a step in an existing campaign sequence. Requires user confirmation for email or content step changes.
Inputs
campaignIdstringrequired- The campaign ID (required for status verification)
sequenceIdstringrequired- The sequence ID (starts with seq_)
stepIdstringrequired- The step ID to update (starts with stp_)
userConfirmedbooleanrequired- REQUIRED: Must be true. Confirms user explicitly approved this change after seeing the preview. Only set to true after user says yes.
altMessagestring- Depends on the step type. On "linkedinInvite": the PREMIUM invitation note (max 300 chars) attached to the connection request when a sender has LinkedIn Premium. On "linkedinSend" and "linkedinVoiceNote": the out-of-network note (200 chars for a standard account), sent as a connection request instead of the message when the lead is not a 1st-degree connection. Pass an empty string to clear it. Ignored on every other step type.
altMessagePremiumstring- "linkedinSend" steps only: the PREMIUM variant of the out-of-network note (max 300 chars), sent instead of altMessage when the sending LinkedIn account is Premium or Sales Navigator. Pass an empty string to clear it. Ignored on every other step type.
callStatusFilterarray- Only for aircallDone ("Call status") condition. Call status/disposition KEYS this branch matches (e.g. ["connected-positive","no-answer"]). Keys are team-specific — call get_call_statuses first. Omit to match any completed call.
ccarray- CC email addresses for email steps, applied to every send of this step. Pass the full desired list (it replaces the current CC); pass an empty array to remove all CC. Ignored on non-email steps.
conditionKeystring- New condition key (for conditional steps only)one of
hasEmailAddresshasLinkedinUrlhasPhoneNumbercontactListcustomLeadInfohasScoreemailsOpenedemailsClickedemailsUnsubscribedmeetingBookedlinkedinInviteAcceptedlinkedinOpenedaircallDonelinkedinNetworkCheckhasWhatsappAccount customFieldstring- Only for customLeadInfo condition. customField is a lead variable or a contact custom-field NAME (the stored name, not its display label), written bare or as "variables.<name>" — the campaign editor writes "variables.". Use "fields.<name>" only for the contact fields that have no lead-variable form, listed by get_condition_field_options (leadStatus, industry, location…). A company field is written "company.<field>", listed under companyFields; "companyName" or "variables.companyName" is stored as "company.name". A field that does not exist for the campaign is rejected, unless the campaign has no lead variables yet, in which case it is accepted and checked when the campaign launches. Call get_condition_field_options first. Omit to keep the current field when only changing the value.
customNamestring- The step's own name, shown instead of its type label in the app (max 70 characters). Ignored on conditional steps. Pass "" to go back to the type label.
customOperatorstring- Only for customLeadInfo condition. Comparison operator: "equal", "contains", "empty", "notEmpty". Omit to keep the current operator.one of
equalcontainsemptynotEmpty customValuestring- Only for customLeadInfo condition. Compared value (required for "equal"/"contains", omitted for "empty"/"notEmpty").
delaynumber- New delay value in days
delayTypestring- New delay type for conditional steps. "within" = must happen within delay days, "waitUntil" = wait indefinitely. For field-check conditions, use "within" with delay 0.one of
withinwaitUntil emailStatusFilterarray- Only for hasEmailAddress condition. Filter by email status: ["deliverable","risky","undeliverable","unverified"].
leadActionstring- "sendToAnotherCampaign" steps only: what happens to the lead in THIS campaign once it has been moved to the target one. "continue" keeps it running the remaining steps, "pause" pauses it, "stop" ends the campaign for it. Omitted leaves the step unset, which behaves as "continue"; the lemlist UI defaults a newly added step to "stop" (shown as "End"). A transfer that fails always pauses the lead, whatever this says.one of
continuepausestop manualboolean- Toggle "Automated" ↔ "Manual step" on togglable channel steps (email, linkedinSend, linkedinInvite, linkedinVoiceNote, whatsappMessage, sms). Set to true to turn the step into a per-lead task (pair with title/priority); set to false to switch it back to an auto-send. Rejected on auto-only step types (linkedinVisit, linkedinFollow, api, enrichment, ...).
messagestring- New message content (supports Liquid syntax): the HTML body for email steps, plain text for LinkedIn steps (linkedinInMail included: a blank one blocks the campaign launch).
methodstring- New HTTP method for "api" (webhook) steps. Limited to GET/POST to stay iso with the lemlist UI editor. Ignored on non-api steps.one of
GETPOST prioritynumber- Task priority for a manual step: 0 = Low, 1 = Medium, 2 = High. Only meaningful when the step is manual (either you also send manual:true, the step already has manual=true, or the step is natively manual — phone/manual).one of
012 scoreComparatorstring- Only for hasScore condition. "$gte" = score above or equal, "$lt" = score below.one of
$gte$lt scoreThresholdnumber- Only for hasScore condition. Score threshold (0-100).
subjectstring- New subject line for email and linkedinInMail steps (supports Liquid syntax: {{ firstName }}, {% if condition %}...{% endif %}). On an email, pass an empty string to remove it: the email then replies on the thread of the previous email. A blank linkedinInMail subject blocks the campaign launch.
titlestring- New task title (for manual/phone steps). Short label displayed in the review UI.
urlstring- New webhook URL for "api" steps. Supports Liquid templating against lead variables (e.g. https://example.com/leads/{{ _id }}). Ignored on non-api steps.
lemlistmcp_update_settingsUpdate settings for a campaign or warmup mailbox entity.DestructiveUpdate Settings
Update settings for a campaign or warmup mailbox entity.
Inputs
itemsarrayrequired- Settings updates to apply, one per entity. Always an array, even for a single update. Items may mix types.
No tools match.