First-party app
lemlist

lemlist

Manage lemlist cold-email campaigns, leads, activities, senders, schedules and unsubscribes via the lemlist API.

stable MarketingEmail

About

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.

App id
io.w6w.lemlist
Version
0.1.2
Author
w6w
Licence
MIT
Categories
Marketing · Email

Overview

lemlist 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.

Build with lemlist

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

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-unsubscribe

Add an email, domain, LinkedIn URL or phone number to the team's unsubscribe list. Idempotent per lemlist.

Delete Lead from Campaign

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

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

get-campaign

Fetch one campaign by id, including its senders, sequence id, schedule ids and any blocking errors.

Get Lead by Email

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

get-team

Fetch the team behind the API key, including the members array (userId, name, email, role) and configured webhooks.

Get Team Credits

get-team-credits

Read the team's remaining enrichment credits, broken down by freemium, subscription, gifted and paid.

List Activities

list-activities

List campaign activity history — opens, replies, step completions — filtered by type, campaign, lead or date range.

List Campaign Leads

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-campaigns

List the team's campaigns, one offset page at a time. Filter by status or creator.

List Schedules

list-schedules

List the team's sending schedules — timezone, start/end window, weekdays and the delay between sends.

List Team Senders

list-team-senders

List the team's sending users, each with the campaigns they send for and those campaigns' sending channels.

List Unsubscribes

list-unsubscribes

List the team's unsubscribed values — emails, domains, LinkedIn URLs and phone numbers — with the source of each opt-out.

Mark Lead as Interested

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 as Not Interested

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-lead

Pause a lead so the sequence stops sending to them. Scoped to one campaign when Campaign id is set, otherwise everywhere.

Resume Paused Lead

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>"
      }
    }
  ]
}

Here are some of the things you can do

  • Add Lead to Campaign

    perform
    add-lead-to-campaign
  • Get Campaign

    read
    get-campaign
  • Get Lead by Email

    read
    get-lead
  • Get Team

    read
    get-team
  • Get Team Credits

    read
    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.

Install
npm install @w6w/sdk
yarn add @w6w/sdk
pnpm add @w6w/sdk
deno add npm:@w6w/sdk
Code
import { 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);
Install the CLI
npm install -g @w6w/cli
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.

What the agent gets

Credentials it can't read

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.

A tool surface scoped to the caller

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.

A durable workflow in one call

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.

Health-aware discovery

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).

Request MCP access

Health checks

lemlist declares its own checks, so its health is a property of the app rather than something the host guesses at.

service

lemlist platform status

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.

quota

Enrichment credits and rate-limit headroom

Reads GET /team/credits for the team's remaining enrichment credits, and the X-RateLimit-* headers on that same response for request headroom.