{
"_id": "stp_V0JHmSpWiO0rxHUkz",
"type": "linkedinInvite",
"delay": 2,
"emailTemplateId": "etp_sw6csWaZNwnfdwGZ6",
"message": "Hello, I would like..."
}"Bad team""The authentication you supplied is incorrect"{
"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..."
}"Bad team""The authentication you supplied is incorrect"{
"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.
customOperator is one of equal, contains, empty, notEmpty. The first two need a
customValue; the last two refuse one.
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 | message, index, delay |
phone | - | message, index, delay |
api | method, url | index, delay |
linkedinVisit | - | index, delay |
linkedinInvite | - | message, altMessage, images, videos, index, delay |
linkedinSend | message | altMessage, altMessagePremium, images, videos, index, delay |
linkedinVoiceNote | - | message, altMessage, index, delay, recordMode |
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 |
sms | message | index, delay |
delayType is not "within", the delay field is not required.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. 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, linkedinVoiceNote, linkedinFollow, linkedinLikeLastPost, linkedinCommentLastPost, linkedinEndorse, linkedinWithdrawInvitation, sendToAnotherCampaign, conditional, whatsappMessage, sms |
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, Optional) | For email steps only. 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, 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, 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) | A title or label used in manual steps. Maximum 400 characters |
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. $ 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) |
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.
Inserting before an existing step on a running campaign is being rolled out progressively. A team
that does not have it yet gets 409 SEQUENCE_STEP_INSERT_ON_SEQUENCE_IN_USE on a non-tail index
and 409 SEQUENCE_STEP_ADD_ON_LOCKED_SEQUENCE on a branch no lead can reach anymore; calling again
without index appends the step.
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, linkedinVoiceNote, linkedinFollow, linkedinLikeLastPost, linkedinCommentLastPost, linkedinEndorse, linkedinWithdrawInvitation, sendToAnotherCampaign, conditional, whatsappMessage, sms 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. Teams without live campaign editing can only append once leads have entered, and a non-tail index is refused with 409 SEQUENCE_STEP_INSERT_ON_SEQUENCE_IN_USE
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.
Content of the email or message (used for email, linkedinInvite, linkedinSend, manual, phone, whatsappMessage, sms steps, and as the AI script of a linkedinVoiceNote step in ai record mode). Required for linkedinSend, whatsappMessage, and sms
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
Title or label for manual steps
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. $ 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 Was this page helpful?