> ## Documentation Index
> Fetch the complete documentation index at: https://developer.lemlist.com/llms.txt
> Use this file to discover all available pages before exploring further.

> Creates a step or condition within a campaign sequence.

# Add Step to Sequence

export const SnippetObjectReference = ({objectName, objectPath = null}) => {
  const lowerCaseObjectName = objectName.toLowerCase();
  if (lowerCaseObjectName === 'lead' || lowerCaseObjectName === 'leads') {
    return <Note>
        This endpoint uses the <a href={`/api-reference/objects-definitions/${objectPath}`}>{objectName} object</a>. Make sure to also check the <a href={`/api-reference/objects-definitions/${lowerCaseObjectName === 'lead' ? 'contact' : 'lead'}`}>{lowerCaseObjectName === 'lead' ? 'Contact' : 'Lead'} object</a> to understand the distinction between the two.
      </Note>;
  }
  return <Note>
      This endpoint uses the <a href={`/api-reference/objects-definitions/${objectPath}`}>{objectName} object</a>.
    </Note>;
};

<SnippetObjectReference objectName="Sequence" objectPath="sequence" />

If you want to get the main sequence or a list of sequences for a campaign, you can call `/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:

```json theme={"theme":"dracula"}
{
    "type": "linkedinInvite",
    "message": "Invite message...."
}
```

## Adding Conditions

To add a condition to a sequence, you must provide the `conditionKey` 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:

```json theme={"theme":"dracula"}
{
    "type": "conditional",
    "conditionKey": "linkedinInviteAccepted",
    "delayType": "waitUntil"
}
```

This will return:

```json theme={"theme":"dracula"}
{
    "_id": "stp_Ae93hiemDkypHLys2",
    "type": "conditional",
    "conditions": [
        {
            "sequenceId": "seq_jacL5GNH3YpNnuNQ2",
            "label": "Accepted invite",
            "key": "linkedinInviteAccepted",
            "delay": 1,
            "delayType": "waitUntil"
        },
        {
            "sequenceId": "seq_xzrGLxhZwoo5oxukc",
            "fallback": true
        }
    ]
}
```

Then you can call `/api/sequences/seq_jacL5GNH3YpNnuNQ2/steps` to add a send step if the invite is accepted:

```json theme={"theme":"dracula"}
{
    "type": "linkedinSend",
    "message": "Hello, ..."
}
```

## Branching on a lead variable

The `customLeadInfo` condition branches on a lead variable or a contact field instead of on a lead
action. It takes three extra fields:

```json theme={"theme":"dracula"}
{
    "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.

<Note>
  A condition step created here has one branch plus the Else fallback. To give it more branches — "CEO
  or Founder here, CTO there, everyone else in Else" — use the
  [condition branch endpoints](/api-reference/endpoints/sequences/list-condition-branches).
</Note>

## 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 common `type` 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` |

<Note>
  In conditional steps, if the `delayType` is not `"within"`, the `delay` field is not required.
</Note>

<Note>
  `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.
</Note>

## 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](#linkedin-invitation-notes) below |
| `altMessagePremium` (String, Conditional) | For `linkedinSend` steps only. The premium variant of the out-of-network note. See [LinkedIn invitation notes](#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](#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](#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](#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

A `sendToAnotherCampaign` 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 |

<Note>
  A transfer that fails always **pauses** the lead, whatever `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.
</Note>

<Note>
  Omitting `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.
</Note>

<Note>
  `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.
</Note>

```json theme={"theme":"dracula"}
{
    "type": "sendToAnotherCampaign",
    "campaignId": "cam_target123",
    "leadAction": "stop"
}
```

## LinkedIn invitation notes

Three LinkedIn step types carry a second note besides `message`, 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 |

```json theme={"theme":"dracula"}
{
    "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."
}
```

LinkedIn caps a connection-request note at 200 characters on a free account and 300 on Premium or
Sales Navigator. These endpoints do not enforce those caps, but an over-long note is a blocking
step error: the campaign refuses to launch until you shorten it.

Sending either field here on a step type that does not take it is rejected with `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.

The move is applied to the campaign's leads right after the response. While it runs, another
structural change on the same campaign answers `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 pass `images` 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` |

If ingestion fails, the API returns a `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 |


## OpenAPI

````yaml post /sequences/{sequenceId}/steps
openapi: 3.0.0
info:
  title: lemlist API
  version: 1.0.0
  description: >-
    Welcome to the lemlist Developer Documentation.


    lemlist is very customizable and open. You'll find on this page all the API
    and integration you can do with lemlist.


    # Rate Limit


    lemlist's API rate limits requests in order to prevent abuse and overload of
    our services.  

    Rate limits are applied on all routes and per API key performing the
    request.  

    The rate limits are **20** requests per **2** seconds.  

    The response provides any information you may need about it:


    | Header | Description |

    | --- | --- |

    | Retry-After | The number of seconds in which you can retry |

    | X-RateLimit-Limit | The maximum requests in that time |

    | X-RateLimit-Remaining | The number of remaining requests you can make |

    | X-RateLimit-Reset | The date when the rate limit will reset |


    _Example of values for the rate limit headers_


    ``` json

    {
        "Retry-After": 2,
        "X-RateLimit-Limit": 20,
        "X-RateLimit-Remaining": 7,
        "X-RateLimit-Reset" : "Tue Feb 16 2021 09:02:42 GMT+0100 (Central European Standard Time)"
    }

     ```

    # Definitions


    ## Team


    A team is the entity of lemlist that can handle users and billing.


    ## Credits


    Credits are the coins a team uses to enrich emails, LinkedIn URLs, etc. via
    the enrich route. Each enrichment feature needs a certain amount of credits
    to run.


    ## User


    You use a user account to connect to lemlist and send messages via the
    connected emails or LinkedIn account.


    ## Campaign


    A campaign is the entity to automate outreach. A campaign has multiple
    sequences composed of steps.


    ## Lead


    A lead is a person that you try to contact via a campaign.


    ## Activity


    An activity is the history of all the steps.


    ## Unsubscribe


    An unsubscribe occurs when a person decides they don't want to receive
    emails from you anymore.


    # Authentication


    All API routes use the dedicated subdomain `api.lemlist.com`.


    lemlist uses API keys to allow access to the API. You can get your lemlist
    API key at our [integration
    page](https://app.lemlist.com/settings/integrations).


    You need to add the `Authorization` header using the `Basic` authentication
    type. `login:password` **where the login is always empty and the password is
    the API key**.


    ⚠️ **Don't forget to add the semicolon (**`:`**) before your API key in curl
    command.**


    > To authorize, use this code: 
      

    ``` shell

    curl https://api.lemlist.com/api/team \
      --user ":YourApiKey"

     ```

    **Make sure to replace** **`YourApiKey`** **with your API key.**


    # Give feedback


    If you want to report a bug, ask for data, or share with us a use case,
    please fill this [form](https://lemlist.typeform.com/to/mfVlkyGf). It will
    help us centralize your needs!
servers:
  - url: https://api.lemlist.com/api
security:
  - basicAuth: []
tags:
  - name: Enrichment Providers
    description: >-
      The data providers lemlist can call to find an email or a phone.


      Internal providers are paid with lemlist credits and always available.
      External providers run on your own account: connect one by saving its API
      key, and it becomes available to your waterfalls. `GET
      /enrichments/providers` is the catalog of provider ids accepted by the
      waterfall endpoints.
  - name: Enrichment Waterfalls
    description: >-
      Control which data providers lemlist calls when enriching a contact, and
      in which order.


      A waterfall is an ordered list of providers for one enrichment `type`
      (`email` or `phone`). Providers are tried one after the other until one
      returns a result.


      Every team has exactly one default waterfall per type (`isDefault: true`).
      While its `editor` is `lemlist` it follows the lemlist behaviour:
      providers you connected with your own API key are tried first, then
      internal providers in a random order. Once a team member edits its
      provider list, `editor` becomes that user's id and the list is followed as
      is; resetting it hands it back to `lemlist`.


      Custom waterfalls (`isDefault: false`) carry `conditions` on the contact
      being enriched, keyed by contact property; every key present must match.
      When a contact is enriched, lemlist runs a single waterfall: the custom
      waterfall of the requested type whose conditions match the contact, or the
      default waterfall when none does. If several match, the one with the most
      condition keys wins, then the most recently updated. Only that waterfall
      runs; when it finds nothing, the enrichment ends without falling back to
      another one.
paths:
  /sequences/{sequenceId}/steps:
    parameters:
      - name: sequenceId
        in: path
        required: true
        description: The unique identifier of the sequence
        example: seq_lkCSKd32qSZZTe5Ru
        schema:
          type: string
    post:
      tags:
        - Sequences
      summary: Add Step to Sequence
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  description: The type of step to create
                  enum:
                    - email
                    - manual
                    - phone
                    - api
                    - linkedinVisit
                    - linkedinInvite
                    - linkedinSend
                    - linkedinVoiceNote
                    - linkedinFollow
                    - linkedinLikeLastPost
                    - linkedinCommentLastPost
                    - linkedinEndorse
                    - linkedinWithdrawInvitation
                    - sendToAnotherCampaign
                    - conditional
                    - whatsappMessage
                    - sms
                index:
                  type: integer
                  description: >-
                    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:
                  type: integer
                  description: >-
                    Delay in days before executing this step. Defaults to 0 for
                    the first step and 1 for subsequent steps
                subject:
                  type: string
                  description: >-
                    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.
                message:
                  type: string
                  description: >-
                    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
                altMessage:
                  type: string
                  description: >-
                    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
                altMessagePremium:
                  type: string
                  description: >-
                    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:
                  type: string
                  description: Title or label for manual steps
                method:
                  type: string
                  description: HTTP method for API steps
                  enum:
                    - GET
                    - POST
                    - PUT
                    - DELETE
                    - PATCH
                url:
                  type: string
                  description: >-
                    URL of the API endpoint to call (required for api steps).
                    Must start with http:// or https://
                conditionKey:
                  type: string
                  description: Condition key for conditional steps
                  enum:
                    - hasEmailAddress
                    - hasLinkedinUrl
                    - hasPhoneNumber
                    - customLeadInfo
                    - hasScore
                    - emailsOpened
                    - emailsClicked
                    - emailsUnsubscribed
                    - meetingBooked
                    - linkedinInviteAccepted
                    - linkedinOpened
                    - aircallDone
                    - linkedinNetworkCheck
                    - hasWhatsappAccount
                delayType:
                  type: string
                  description: Delay type for conditional steps
                  enum:
                    - within
                    - waitUntil
                customField:
                  type: string
                  description: >-
                    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.
                customOperator:
                  type: string
                  description: >-
                    For conditional steps keyed `customLeadInfo` only. How the
                    field is compared.
                  enum:
                    - equal
                    - contains
                    - empty
                    - notEmpty
                customValue:
                  type: string
                  description: >-
                    For conditional steps keyed `customLeadInfo` only. The value
                    the field is compared to. Required for `equal` and
                    `contains`, and refused for `empty` and `notEmpty`.
                campaignId:
                  type: string
                  description: >-
                    Target campaign ID for sendToAnotherCampaign steps. The
                    campaign must exist and not be archived
                leadAction:
                  type: string
                  description: >-
                    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.
                  enum:
                    - continue
                    - pause
                    - stop
                images:
                  type: array
                  items:
                    type: string
                    format: uri
                  description: >-
                    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`.
                videos:
                  type: array
                  items:
                    type: string
                    format: uri
                  description: >-
                    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`.
                skillName:
                  type: string
                  description: >-
                    Name of the LinkedIn skill to endorse on the lead's profile.
                    Applies to `linkedinEndorse` steps only.
                endorseAnyFallback:
                  type: boolean
                  description: >-
                    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.
                recordMode:
                  type: string
                  description: >-
                    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.
                  enum:
                    - manual
                    - ai
            examples:
              Email Step:
                value:
                  type: email
                  subject: '{{firstName}}, quick question about {{companyName}}'
                  message: >-
                    <p>Hi {{firstName}},</p><p>I noticed {{companyName}} is
                    working on improving customer engagement. Would you be
                    interested in discussing how we can help?</p><p>Best
                    regards,<br>{{senderName}}</p>
                  delay: 1
                  index: 2
              LinkedIn Invite:
                value:
                  type: linkedinInvite
                  message: >-
                    Hi {{firstName}}, I'd love to connect and learn more about
                    your work at {{companyName}}!
                  delay: 0
              LinkedIn Send:
                value:
                  type: linkedinSend
                  message: >-
                    Hey {{firstName}}, thanks for connecting! I wanted to reach
                    out about...
                  delay: 2
              LinkedIn Send with Media:
                value:
                  type: linkedinSend
                  message: >-
                    Hey {{firstName}}, sharing a quick overview of what we're
                    building.
                  images:
                    - https://example.com/assets/overview.png
                  videos:
                    - https://example.com/assets/demo.mp4
                  delay: 2
              Manual Task:
                value:
                  type: manual
                  title: Call prospect
                  message: Discuss their pain points and schedule a demo
                  delay: 1
              Phone Call:
                value:
                  type: phone
                  message: Follow up on the email sent yesterday
                  delay: 1
              API Call:
                value:
                  type: api
                  method: POST
                  url: https://api.example.com/webhook
                  delay: 0
              LinkedIn Visit:
                value:
                  type: linkedinVisit
                  delay: 0
              Conditional Step:
                value:
                  type: conditional
                  conditionKey: linkedinInviteAccepted
                  delayType: waitUntil
                  delay: 1
              Custom Lead Info Condition:
                value:
                  type: conditional
                  conditionKey: customLeadInfo
                  delayType: within
                  delay: 1
                  customField: jobTitle
                  customOperator: contains
                  customValue: CEO
              Send to Another Campaign:
                value:
                  type: sendToAnotherCampaign
                  campaignId: cam_ABC123XYZ456
                  delay: 0
              WhatsApp Message:
                value:
                  type: whatsappMessage
                  message: Hi {{firstName}}, following up on our conversation...
                  delay: 1
      responses:
        '200':
          headers:
            Content-Type:
              schema:
                type: string
              example: application/json
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  _id:
                    type: string
                  type:
                    type: string
                  delay:
                    type: integer
                  emailTemplateId:
                    type: string
                  message:
                    type: string
              example:
                _id: stp_V0JHmSpWiO0rxHUkz
                type: linkedinInvite
                delay: 2
                emailTemplateId: etp_sw6csWaZNwnfdwGZ6
                message: Hello, I would like...
        '400':
          description: 'Possible errors: Bad team / Error from createStep'
          content:
            text/plain:
              example: Bad team
        '401':
          description: The authentication you supplied is incorrect
          content:
            text/plain:
              example: The authentication you supplied is incorrect
        '405':
          description: Method not allowed
        '409':
          description: >-
            `campaign-reroute-in-progress` - a previous step change on this
            campaign is still being applied to its leads; nothing was written,
            retry in a minute. `SEQUENCE_STEP_INSERT_ON_SEQUENCE_IN_USE` /
            `SEQUENCE_STEP_ADD_ON_LOCKED_SEQUENCE` - leads have entered the
            campaign and the team does not have live campaign editing, so the
            step can only be appended to a sequence leads still reach.
          content:
            application/json:
              example:
                error: >-
                  A step is still being added for the leads of this campaign.
                  Retry in a minute.
                code: campaign-reroute-in-progress
components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.