Manage lemlist cold-email campaigns, leads, activities, senders, schedules and unsubscribes via the lemlist API.
lemlist ships in the w6w first-party pack. It declares 18 actions, 2 health checks, and the host runs its code in a sandbox that never sees the credential.
io.w6w.lemlistlemlist enrolls and manages leads in a cold-email sequence from inside a workflow — advancing them through interested, not-interested, paused and resumed states as a campaign’s own automation runs, and reading back the activity that got them there.
Leads can be added to or removed from a campaign, marked interested or not interested, and paused or resumed, with activity history filterable by type, lead and date range for reporting. Campaign, sender and schedule details are all readable so a workflow can pick the right sequence and sending window before it acts, and the team’s enrichment credits are visible alongside the rest of its data. Suppression is a first-class action too — unsubscribing a variable (an email, domain, LinkedIn URL or phone number) and reading back the current suppression list.
It fits outbound sales workflows that need to enrol leads from another system, react to a reply or an interested mark, or keep a suppression list in sync across tools — anywhere lemlist’s own sequences need to start, pause or branch based on what happens elsewhere.
Three routes to the same 18 actions. The Workflow tab is generated from lemlist's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.
add-lead-to-campaign Create a lead in a campaign. Any extra key you pass under Custom variables is stored on the lead and usable in the sequence as {{name}}.
add-unsubscribe Add an email, domain, LinkedIn URL or phone number to the team's unsubscribe list. Idempotent per lemlist.
delete-lead-from-campaign Remove a lead from a campaign. Defaults to a true delete (`action=remove`); clearing Action makes lemlist UNSUBSCRIBE the lead instead, which is team-wide.
delete-unsubscribe Re-subscribe a value by removing it from the unsubscribe list. lemlist refuses (409) for values whose source is `lead` or `abuse`.
get-campaign Fetch one campaign by id, including its senders, sequence id, schedule ids and any blocking errors.
get-lead Look a lead up by email. Returns one record per campaign the address belongs to, each with its own state, status and variables.
get-team Fetch the team behind the API key, including the members array (userId, name, email, role) and configured webhooks.
get-team-credits Read the team's remaining enrichment credits, broken down by freemium, subscription, gifted and paid.
list-activities List campaign activity history — opens, replies, step completions — filtered by type, campaign, lead or date range.
list-campaign-leads List the leads inside one campaign, optionally filtered by state. lemlist offers no offset here — use `state` to narrow.
list-campaigns List the team's campaigns, one offset page at a time. Filter by status or creator.
list-schedules List the team's sending schedules — timezone, start/end window, weekdays and the delay between sends.
list-team-senders List the team's sending users, each with the campaigns they send for and those campaigns' sending channels.
list-unsubscribes List the team's unsubscribed values — emails, domains, LinkedIn URLs and phone numbers — with the source of each opt-out.
mark-lead-interested Mark a lead interested. Scoped to one campaign when Campaign id is set, otherwise applied across every campaign the lead is in.
mark-lead-not-interested Mark a lead not interested. Scoped to one campaign when Campaign id is set, otherwise applied across every campaign the lead is in.
pause-lead Pause a lead so the sequence stops sending to them. Scoped to one campaign when Campaign id is set, otherwise everywhere.
resume-lead Resume a lead that was paused, so the sequence continues. The counterpart to Pause Lead.
A workflow step names the app and the action, and the editor fills in the
connection when you pick one. This is the Step shape from the
workflow spec, carrying lemlist's real ids.
{
"manifestVersion": "2",
"name": "lemlist-example",
"steps": [
{
"id": "add-lead-to-campaign",
"uses": {
"app": "io.w6w.lemlist",
"action": "add-lead-to-campaign",
"connection": "conn_YOUR_CONNECTION_ID"
},
"with": {
"campaignId": "<campaignId>"
}
}
]
}add-lead-to-campaign get-campaign get-lead get-team get-team-credits +13 more actions available
Every app-specific value here is a blank you have to fill in. An
app action is reached through the connection that authenticates it, so the
address is a connection id, not the app id — and connections belong to your account,
so a public page cannot know yours. Create one for lemlist, then fill in
the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the
parameters that action declares. The call itself is real — the shape is transcribed
from the studio's own snippet builder, which prints the same kind of blanks — but
nothing in it is specific to lemlist. The Workflow tab is where this app's
real ids are.
npm install @w6w/sdkyarn add @w6w/sdkpnpm add @w6w/sdkdeno add npm:@w6w/sdkimport { W6wClient, isActionRun } from "@w6w/sdk";
// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();
const envelope = await client.run({
urn: "conn_YOUR_CONNECTION_ID",
action: "add-lead-to-campaign",
payload: {
campaignId: "<value>",
// email: "<value>",
// firstName: "<value>",
// lastName: "<value>",
// companyName: "<value>",
// jobTitle: "<value>",
// linkedinUrl: "<value>",
// phone: "<value>",
// companyDomain: "<value>",
// picture: "<value>",
// icebreaker: "<value>",
// timezone: "<value>",
// contactOwner: "<value>",
// customVariables: "<value>",
// enrichment: "<value>",
},
});
if (isActionRun(envelope)) console.log(envelope.value); npm install -g @w6w/cli w6w run conn_YOUR_CONNECTION_ID --action add-lead-to-campaign --payload '{"campaignId":"<value>"}' Give an AI agent lemlist — without giving it lemlist's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.
One tool call{
"name": "w6w_invoke",
"arguments": {
"ref": "app:io.w6w.lemlist#add-lead-to-campaign",
"input": {
"campaignId": "<campaignId>"
}
}
}
Every tool names its target with a single ref. The
app: form above doesn't name a connection at all — the
host resolves which of the caller's lemlist connections to sign
with, and refuses rather than guesses when the answer is ambiguous.
The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.
Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.
Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.
lemlist's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.
The MCP surface is part of the hosted platform. lemlist itself is MIT, and the runtime that executes it is source-available (FSL).
lemlist declares its own checks, so its health is a property of the app rather than something the host guesses at.
Hyperping rollup for status.lempire.com, lempire's status page — it monitors lemlist and the lemlist API alongside lemwarm, taplio and tweethunter. Unauthenticated and unsigned.
Reads GET /team/credits for the team's remaining enrichment credits, and the X-RateLimit-* headers on that same response for request headroom.