{
"_id": "stp_V0JHmSpWiO0rxHUkz",
"type": "linkedinInvite",
"delay": 2,
"emailTemplateId": "etp_sw6csWaZNwnfdwGZ6",
"message": "Hello, I would like..."
}{
"error": "Invalid request body"
}The authentication you supplied is incorrect{
"error": "Upgrade your plan to use this feature",
"code": "upper-plan-required"
}{
"error": "A step is still being added for the leads of this campaign. Retry in a minute.",
"code": "campaign-reroute-in-progress"
}Add Step to Sequence
Creates a step or condition within a campaign sequence.
{
"_id": "stp_V0JHmSpWiO0rxHUkz",
"type": "linkedinInvite",
"delay": 2,
"emailTemplateId": "etp_sw6csWaZNwnfdwGZ6",
"message": "Hello, I would like..."
}{
"error": "Invalid request body"
}The authentication you supplied is incorrect{
"error": "Upgrade your plan to use this feature",
"code": "upper-plan-required"
}{
"error": "A step is still being added for the leads of this campaign. Retry in a minute.",
"code": "campaign-reroute-in-progress"
}/api/campaigns/{campaignId}/sequences.
Adding Steps
To add a new step to a sequence, call the API with the sequence ID in the parameters and the step details in the request body. For example, adding a LinkedIn invite step:{
"type": "linkedinInvite",
"message": "Invite message...."
}
Adding Conditions
To add a condition to a sequence, you must provide theconditionKey and type conditional along with the required fields. The API will return a condition with an array of condition sequences, where you can call the same API again to add steps to those sequences.
For example, creating a LinkedIn invite condition:
{
"type": "conditional",
"conditionKey": "linkedinInviteAccepted",
"delayType": "waitUntil"
}
{
"_id": "stp_Ae93hiemDkypHLys2",
"type": "conditional",
"conditions": [
{
"sequenceId": "seq_jacL5GNH3YpNnuNQ2",
"label": "Accepted invite",
"key": "linkedinInviteAccepted",
"delay": 1,
"delayType": "waitUntil"
},
{
"sequenceId": "seq_xzrGLxhZwoo5oxukc",
"fallback": true
}
]
}
/api/sequences/seq_jacL5GNH3YpNnuNQ2/steps to add a send step if the invite is accepted:
{
"type": "linkedinSend",
"message": "Hello, ..."
}
Branching on a lead variable
ThecustomLeadInfo condition branches on a lead variable or a contact field instead of on a lead
action. It takes three extra fields:
{
"type": "conditional",
"conditionKey": "customLeadInfo",
"delayType": "within",
"delay": 1,
"customField": "jobTitle",
"customOperator": "contains",
"customValue": "CEO"
}
customField reads a bare name as a lead variable — jobTitle becomes variables.jobTitle. Prefix
it with fields. to test a contact field instead.
A company field is written company.<field> — company.name, or company.sector for a company
custom field named Sector. companyName and variables.companyName are stored as company.name.
customOperator is one of equal, contains, empty, notEmpty. The first two need a
customValue; the last two refuse one.
Manual steps
A step that takesmanual sends automatically by default. With manual: true it creates a task
first, and the campaign sends the step on its schedule once that task is marked done, in lemlist or
with Update Task. To close the task without sending,
use Ignore Tasks: the lead moves on to the next step.
message of Update Task
does not change what the campaign sends.
A linkedinVoiceNote step needs its recording, unless its recordMode is ai. Attach it with
Upload Audio or on the task in lemlist before marking the task done: without one, nothing
is sent, the step fails for that lead, and its following LinkedIn steps are skipped.
title names the task and priority ranks it: 0 low, 1 medium, 2 high. Omitted, the task has
no priority.
{
"type": "linkedinSend",
"message": "Thanks for connecting, {{firstName}}!",
"manual": true,
"title": "Personalize the intro message",
"priority": 2
}
| Step type | manual | title, priority |
|---|---|---|
email, linkedinInvite, linkedinSend, linkedinInMail, linkedinVoiceNote, whatsappMessage, sms | Accepted | Accepted, and applied once the step is manual |
manual, phone | Rejected: they are always tasks | Accepted. title is required on manual |
| Every other type | Rejected | Rejected |
400 Invalid request body. To switch an existing step, see
Update Sequence Step.
Enrichment steps
Anenrichment step enriches each lead that reaches it. workflowIds lists what it looks up, with
at least one value:
| Value | Looks up | Credits |
|---|---|---|
find_email_01 | The lead’s email address | 5 per email found |
find_phone_01 | The lead’s phone number | 20 per phone number found |
verify_01 | The deliverability of the lead’s email | 1 per email verified |
linkedin_01 | The lead’s LinkedIn profile | 1 per profile enriched |
{
"type": "enrichment",
"workflowIds": ["find_email_01", "verify_01"],
"delay": 0
}
- A lead with nothing left to look up skips the step at no cost: with
find_phone_01alone, a lead whose contact already has a phone number. - Otherwise the step’s workflows run together, so data the contact already holds can be charged again.
find_email_01searches again on a lead that already has an email, so a step with it rarely skips a lead.- With both
find_email_01andverify_01, only one runs:verify_01on a lead with a correctly formatted email, even one known as undeliverable,find_email_01otherwise. In the example above, a lead with a correctly formatted email costs at most 1 credit, and any other lead at most 5. - When one workflow has work to do, a lead missing what another needs fails the step, and no credits
are charged: with
verify_01andfind_phone_01, a lead with neither an email nor a phone number.
workflowIds answers 400 workflowIds is required, an empty array or an unknown value
answers 400 Invalid request body. workflowIds is set when the step is created:
Update Sequence Step
does not change it.
enrichment steps need the Multichannel Expert plan or above. Below it, the call answers
402 upper-plan-required.Step Types and Required Fields
The table below summarizes the required and optional fields for each step type. Note that all step requests must include a commontype field.
| Step Type | Required Fields | Optional Fields |
|---|---|---|
email | message | subject, index, delay, manual, title, priority |
manual | title | message, index, delay, priority |
phone | - | message, index, delay, title, priority |
api | method, url | index, delay |
linkedinVisit | - | index, delay |
linkedinInvite | - | message, altMessage, images, videos, index, delay, manual, title, priority |
linkedinSend | message | altMessage, altMessagePremium, images, videos, index, delay, manual, title, priority |
linkedinInMail | subject, message | index, delay, manual, title, priority |
linkedinVoiceNote | - | message, altMessage, index, delay, recordMode, manual, title, priority |
linkedinFollow | - | index, delay |
linkedinLikeLastPost | - | index, delay |
linkedinCommentLastPost | - | index, delay |
linkedinEndorse | - | index, delay, skillName, endorseAnyFallback |
linkedinWithdrawInvitation | - | index, delay |
sendToAnotherCampaign | campaignId | leadAction, index |
conditional | conditionKey, delayType (and delay when delayType is within) | index |
whatsappMessage | message | index, delay, manual, title, priority |
sms | message | index, delay, manual, title, priority |
enrichment | workflowIds | index, delay |
delayType is not "within", the delay field is not required.linkedinInMail step sends a LinkedIn InMail, so it reaches leads who are not connected to the
sender — the step to use in the “invite not accepted” branch of a linkedinInviteAccepted
condition. It needs a LinkedIn Premium or Sales Navigator sender: a sender without one is skipped.
Each send spends one of the sender’s InMail credits. A blank subject or message is saved but
blocks the campaign launch, as for the other message steps. Both are sent as plain text: LinkedIn
does not render HTML. The step takes no altMessage, images or videos.linkedinVoiceNote steps are created as skeletons via the API: the audio payload itself is not accepted here and must be added afterwards, from the lemlist UI or for one lead with Upload Audio. The recordMode field controls how the audio is sourced — with manual (default), a user records the note themselves; with ai, a user provides a text template that lemlist converts to audio at send time.All Request Body Fields
| Field | Description |
|---|---|
type (String, Required) | The type of step to create. Allowed values: email, manual, phone, api, linkedinVisit, linkedinInvite, linkedinSend, linkedinInMail, linkedinVoiceNote, linkedinFollow, linkedinLikeLastPost, linkedinCommentLastPost, linkedinEndorse, linkedinWithdrawInvitation, sendToAnotherCampaign, conditional, whatsappMessage, sms, enrichment |
index (Integer, Optional) | The position within the sequence to insert the new step. Must be an integer ≥ -1. If omitted or if the index is greater than the number of steps, the new step is added to the end |
delay (Integer, Optional) | The delay (in days) before executing the step. Must be between 0 and 1500. Defaults to 0 for the first step and to 1 for subsequent steps (except for certain conditional configurations) |
subject (String, Conditional) | For email and linkedinInMail steps. Required on linkedinInMail, where it is the InMail subject: a blank one is saved but blocks the campaign launch. On email, the email subject. Omit it or send an empty string on a follow-up so the email replies in the thread of the previous one. If no email has been sent to the lead in this campaign yet, it goes out with an empty subject. Maximum 400 characters (applied to the raw template, including any Liquid syntax) |
message (String, Conditional) | Content of the email or message. Used for email, linkedinInvite, linkedinSend, linkedinInMail, whatsappMessage, and sms step types, the AI script of a linkedinVoiceNote step in ai record mode, or the note of manual and phone step types. Required for linkedinSend, linkedinInMail, whatsappMessage, and sms |
altMessage (String, Conditional) | For linkedinInvite, linkedinSend and linkedinVoiceNote steps only. The premium invitation note on an invite, the out-of-network note on the other two. See LinkedIn invitation notes below |
altMessagePremium (String, Conditional) | For linkedinSend steps only. The premium variant of the out-of-network note. See LinkedIn invitation notes below |
title (String, Conditional) | The title of the task the step creates. Required on manual steps, optional on phone steps and on a channel step with manual: true. Maximum 100 characters |
manual (Boolean, Optional) | For email, linkedinInvite, linkedinSend, linkedinInMail, linkedinVoiceNote, whatsappMessage and sms steps only. true creates a task, and the campaign sends the step once the task is marked done. See Manual steps above |
priority (Integer, Optional) | The priority of the task the step creates: 0 low, 1 medium, 2 high. For manual and phone steps, and for the channel steps that take manual |
method (String, Conditional) | The HTTP method to use for API steps. Allowed values: GET, POST, PUT, DELETE, PATCH |
url (String, Conditional) | The URL of the API endpoint to call. Must be a valid URL (starting with http:// or https://) |
conditionKey (String, Conditional) | For conditional steps only. Defines the condition key. Allowed values: hasEmailAddress, hasLinkedinUrl, hasPhoneNumber, customLeadInfo, hasScore, emailsOpened, emailsClicked, emailsUnsubscribed, meetingBooked, linkedinInviteAccepted, linkedinOpened, aircallDone, linkedinNetworkCheck, hasWhatsappAccount |
delayType (String, Conditional) | For conditional steps only. Specifies the delay type. Allowed values: within, waitUntil |
customField (String, Conditional) | For customLeadInfo conditions only. The field to test. A bare name reads as a lead variable (jobTitle becomes variables.jobTitle); prefix with fields. to test a contact field; a company field is written company.<field> (companyName is stored as company.name). $ and the reserved keys _id, teamId, campaignId, leadId, __proto__, constructor, prototype are refused |
customOperator (String, Conditional) | For customLeadInfo conditions only. How the field is compared. Allowed values: equal, contains, empty, notEmpty |
customValue (String, Conditional) | For customLeadInfo conditions only. The value the field is compared to. Required for equal and contains, and refused for empty and notEmpty |
campaignId (String, Conditional) | For steps of type sendToAnotherCampaign only. The target campaign ID to which a lead should be sent. The specified campaign must exist in the team and not be archived |
leadAction (String, Optional) | For steps of type sendToAnotherCampaign only. What happens to the lead in the source campaign once it has been moved. Allowed values: continue (keeps running the remaining steps), pause, stop (ends the source campaign for that lead). Omitted leaves the step without a value, which behaves as continue. See What happens to the transferred lead below |
images (Array of strings, Optional) | For linkedinInvite and linkedinSend steps only. Public HTTPS URLs of images to attach to the LinkedIn message. lemlist downloads each file and re-hosts it. See the LinkedIn media constraints below |
videos (Array of strings, Optional) | For linkedinInvite and linkedinSend steps only. Public HTTPS URLs of videos to attach to the LinkedIn message. lemlist downloads each file and re-hosts it. See the LinkedIn media constraints below |
skillName (String, Optional) | For linkedinEndorse steps only. Name of the LinkedIn skill to endorse on the lead’s profile |
endorseAnyFallback (Boolean, Optional) | For linkedinEndorse steps only. When true and the named skill is not on the lead’s profile, lemlist falls back to endorsing any available skill |
recordMode (String, Optional) | For linkedinVoiceNote steps only. Determines how the audio is sourced. Allowed values: manual (user records the audio after step creation; default), ai (audio is generated from a text template provided in the lemlist UI) |
workflowIds (Array of strings, Conditional) | Required on enrichment steps, which are the only ones taking it. What the step looks up for each lead. Allowed values: find_email_01, find_phone_01, verify_01, linkedin_01. See Enrichment steps above |
What happens to the transferred lead
AsendToAnotherCampaign step adds the lead to the target campaign. leadAction decides what the
source campaign then does with it.
| Value | Effect on the lead in the source campaign |
|---|---|
continue | Keeps running the remaining steps (the behaviour of a step with no leadAction) |
pause | Paused. The next step is created but not scheduled; resuming the lead schedules it |
stop | The source campaign is ended for that lead and its pending steps are dropped |
leadAction says, so a lead that never
moved does not walk on as if it had. A transfer fails when the target campaign is missing or
archived, when it has reached its 40,000-lead limit, when the lead’s email address is unsubscribed,
and when the lead is already in the target campaign — a re-run of the same step, or two
campaigns feeding one target, both land there.leadAction leaves the step without a value, which the runtime reads as continue. A step
added from the lemlist UI defaults to stop instead; that default belongs to the editor, not to the
API.leadAction is read when the step runs, not when the lead reaches it. Updating it applies to every
lead still waiting at that step, including leads whose transfer is already queued.{
"type": "sendToAnotherCampaign",
"campaignId": "cam_target123",
"leadAction": "stop"
}
LinkedIn invitation notes
Three LinkedIn step types carry a second note besidesmessage, and altMessage does not mean the
same thing on each of them.
| Field | Step type | What it holds |
|---|---|---|
altMessage | linkedinInvite | The premium invitation note, attached to the connection request instead of message when the sending account has LinkedIn Premium |
altMessage | linkedinSend | The out-of-network note, sent as a connection request instead of the message when the lead is not a 1st-degree connection |
altMessage | linkedinVoiceNote | The same out-of-network note, sent instead of the voice note when the lead is not connected |
altMessagePremium | linkedinSend | The premium variant of the out-of-network note, sent instead of altMessage when the sending account is Premium or Sales Navigator |
{
"type": "linkedinSend",
"message": "Thanks for connecting, {{firstName}}!",
"altMessage": "Hi {{firstName}}, we are not connected yet, happy to change that!",
"altMessagePremium": "Hi {{firstName}}, we are not connected yet. I work with teams like {{companyName}} on the same problem and would love to swap notes."
}
400 Invalid request body.
Running campaigns
A campaign that leads have entered still accepts new steps. Where the step lands decides who receives it:- Appended after the last step of a sequence leads finish on: the leads parked on that last step move on to the new one.
- Inserted before an existing step (with
index): the leads waiting on that step are moved onto the new one and run it first. Leads already past that position never receive it.
409 campaign-reroute-in-progress; nothing is
written and the same call succeeds a minute later.
LinkedIn media constraints
When you passimages or videos to a linkedinInvite or linkedinSend step, each URL must be a publicly reachable HTTPS URL. lemlist fetches the file, validates it, and re-hosts it before attaching it to the LinkedIn message.
| Constraint | Value |
|---|---|
| Maximum items per step | 6 (combined across images + videos) |
| Maximum file size | 20 MB per file |
| Allowed image MIME types | image/png, image/jpeg, image/gif |
| Allowed video MIME types | video/mp4, video/quicktime |
4xx/5xx status with one of the following error codes:
| Code | HTTP status | Meaning |
|---|---|---|
LINKEDIN_MEDIA_TOO_MANY | 400 | More than 6 items submitted |
LINKEDIN_MEDIA_UNSAFE_URL | 400 | URL is not HTTPS or not publicly reachable |
LINKEDIN_MEDIA_TOO_LARGE | 413 | File exceeds 20 MB |
LINKEDIN_MEDIA_INVALID_TYPE | 415 | File MIME type is not allowed |
LINKEDIN_MEDIA_GIF_INVALID | 422 | GIF does not meet LinkedIn’s constraints |
LINKEDIN_MEDIA_DOWNLOAD_FAILED | 502 | lemlist could not download the file |
LINKEDIN_MEDIA_AV_UNAVAILABLE | 503 | Antivirus scanner is unavailable |
LINKEDIN_MEDIA_TIMEOUT | 504 | Ingestion took longer than 45 seconds |
Authorizations
Basic authentication header of the form Basic <encoded-value>, where <encoded-value> is the base64-encoded string username:password.
Path Parameters
The unique identifier of the sequence
Body
The type of step to create
email, manual, phone, api, linkedinVisit, linkedinInvite, linkedinSend, linkedinInMail, linkedinVoiceNote, linkedinFollow, linkedinLikeLastPost, linkedinCommentLastPost, linkedinEndorse, linkedinWithdrawInvitation, sendToAnotherCampaign, conditional, whatsappMessage, sms, enrichment The position within the sequence to insert the new step (≥ -1). If omitted or greater than the number of steps, the new step is added to the end. On a campaign leads have entered, a step inserted before an existing one moves the leads waiting on that step onto the new one; leads already past that position never receive it.
Delay in days before executing this step. Defaults to 0 for the first step and 1 for subsequent steps
Email subject line (for email steps). Optional: omit it or send an empty string on a follow-up so the email replies in the thread of the previous one. If no email has been sent to the lead in this campaign yet, it goes out with an empty subject. Required on linkedinInMail steps, where it is the InMail subject: a blank one is saved but blocks the campaign launch
Content of the email or message (used for email, linkedinInvite, linkedinSend, linkedinInMail, manual, phone, whatsappMessage, sms steps, and as the AI script of a linkedinVoiceNote step in ai record mode). Required for linkedinSend, linkedinInMail, whatsappMessage, and sms. On a linkedinInMail step it is sent as plain text (LinkedIn does not render HTML), and a blank one is saved but blocks the campaign launch
The LinkedIn note sent instead of message in specific cases, with a meaning that depends on the step type. On linkedinInvite: the premium invitation note attached to the connection request, used when the sending account has LinkedIn Premium. On linkedinSend and linkedinVoiceNote: the out-of-network note, sent as a connection request when the lead is not a 1st-degree connection. Rejected with 400 on any other step type. LinkedIn caps a connection-request note at 200 characters on a free account and 300 on Premium or Sales Navigator. This endpoint does not enforce those caps, but an over-long note is a blocking step error: the campaign refuses to launch until it is shortened
For linkedinSend steps only. The premium variant of the out-of-network note, sent instead of altMessage when the sending LinkedIn account is Premium or Sales Navigator (300-character LinkedIn cap). Rejected with 400 on any other step type
The step's own name, shown instead of its type label ("Email", "Call") wherever the app names the step. Plain text, up to 70 characters once trimmed and once runs of spaces collapse to one; HTML tags count toward the 70, then are dropped. A longer name is rejected with 400. Accepted on every step type, ignored on conditional steps, which keep their type label
70Title of the task the step creates. Required on manual steps, optional on phone steps and on a channel step with manual: true. Maximum 100 characters
Holds a channel step behind a task: true creates a task, named by title and ranked by priority, and the campaign sends the step once the task is marked done. A linkedinVoiceNote step also needs its recording, see Manual steps. Accepted on email, linkedinInvite, linkedinSend, linkedinInMail, linkedinVoiceNote, whatsappMessage and sms steps. Rejected with 400 on every other type, manual and phone steps included: they are always tasks
Priority of the task the step creates: 0 low, 1 medium, 2 high. Omitted, the task has no priority. Accepted on manual and phone steps and on the channel steps that take manual, where it applies once the step is manual
0, 1, 2 HTTP method for API steps
GET, POST, PUT, DELETE, PATCH URL of the API endpoint to call (required for api steps). Must start with http:// or https://
Condition key for conditional steps
hasEmailAddress, hasLinkedinUrl, hasPhoneNumber, customLeadInfo, hasScore, emailsOpened, emailsClicked, emailsUnsubscribed, meetingBooked, linkedinInviteAccepted, linkedinOpened, aircallDone, linkedinNetworkCheck, hasWhatsappAccount Delay type for conditional steps
within, waitUntil For conditional steps keyed customLeadInfo only. The field to test. A bare name reads as a lead variable (jobTitle becomes variables.jobTitle); prefix with fields. to test a contact field. A company field is written company.<field> - company.name, or company.sector for a company custom field named Sector - and companyName or variables.companyName is stored as company.name. $ and the reserved keys _id, teamId, campaignId, leadId, __proto__, constructor and prototype are refused.
For conditional steps keyed customLeadInfo only. How the field is compared.
equal, contains, empty, notEmpty For conditional steps keyed customLeadInfo only. The value the field is compared to. Required for equal and contains, and refused for empty and notEmpty.
Target campaign ID for sendToAnotherCampaign steps. The campaign must exist and not be archived
Applies to sendToAnotherCampaign steps only. What happens to the lead in the SOURCE campaign once it has been moved to the target one. continue keeps it running the remaining steps, pause pauses it, stop ends the source campaign for it. Omitted leaves the step without a value, which behaves as continue; a step added from the lemlist UI defaults to stop. A transfer that fails always pauses the lead, whatever this says.
continue, pause, stop Public HTTPS URLs of images to attach to a linkedinInvite or linkedinSend step. lemlist downloads each file and re-hosts it. Allowed MIME types: image/png, image/jpeg, image/gif. Up to 20 MB per file, and up to 6 items total combined with videos.
Public HTTPS URLs of videos to attach to a linkedinInvite or linkedinSend step. lemlist downloads each file and re-hosts it. Allowed MIME types: video/mp4, video/quicktime. Up to 20 MB per file, and up to 6 items total combined with images.
Name of the LinkedIn skill to endorse on the lead's profile. Applies to linkedinEndorse steps only.
Applies to linkedinEndorse steps only. When true and the named skill is not on the lead's profile, lemlist falls back to endorsing any available skill.
Applies to linkedinVoiceNote steps only. Determines how the audio is sourced. manual (default) means the user records the audio themselves from the lemlist UI after step creation; ai means lemlist generates the audio from a text template provided in the lemlist UI.
manual, ai Required on enrichment steps, with at least one value. What the step looks up for each lead that reaches it: find_email_01 (the email address, 5 credits per email found), find_phone_01 (the phone number, 20 credits per phone number found), verify_01 (the deliverability of the email, 1 credit per email verified), linkedin_01 (the LinkedIn profile, 1 credit per profile enriched). A lookup is charged only when it returns data. With both find_email_01 and verify_01, only one runs for a lead: verify_01 if it has a correctly formatted email, find_email_01 otherwise. An unknown value or an empty array is rejected with 400. enrichment steps need the Multichannel Expert plan or above
1find_email_01, find_phone_01, verify_01, linkedin_01 Response
Success
The step's own name, shown instead of its type label ("Email", "Call") wherever the app names the step. Absent when the step has none
Title of the task the step creates. On manual and phone steps, and on a channel step when one was set
true on a channel step held behind a task: the campaign sends the step once the task is marked done. manual and phone steps are always tasks, whatever this field says
Priority of the task the step creates: 0 low, 1 medium, 2 high. Absent when none was set
0, 1, 2 On enrichment steps only. What the step looks up for each lead that reaches it
find_email_01, find_phone_01, verify_01, linkedin_01 Was this page helpful?