> ## 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, ..."
}
```

## 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`                      | `subject`, `message`                                                   | `index`, `delay`                                    |
| `manual`                     | `title`                                                                | `message`, `index`, `delay`                         |
| `phone`                      | -                                                                      | `message`, `index`, `delay`                         |
| `api`                        | `method`, `url`                                                        | `index`, `delay`                                    |
| `linkedinVisit`              | -                                                                      | `index`, `delay`                                    |
| `linkedinInvite`             | -                                                                      | `message`, `images`, `videos`, `index`, `delay`     |
| `linkedinSend`               | `message`                                                              | `altMessage`, `images`, `videos`, `index`, `delay`  |
| `linkedinVoiceNote`          | -                                                                      | `index`, `delay`, `recordMode`                      |
| `linkedinFollow`             | -                                                                      | `index`, `delay`                                    |
| `linkedinLikeLastPost`       | -                                                                      | `index`, `delay`                                    |
| `linkedinCommentLastPost`    | -                                                                      | `index`, `delay`                                    |
| `linkedinEndorse`            | -                                                                      | `index`, `delay`, `skillName`, `endorseAnyFallback` |
| `linkedinWithdrawInvitation` | -                                                                      | `index`, `delay`                                    |
| `sendToAnotherCampaign`      | `campaignId`                                                           | `index`, `delay`                                    |
| `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, Conditional)          | The email subject. Required for steps of type `email`. 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, or the note of `manual` and `phone` step types. Required for `linkedinSend`, `whatsappMessage`, and `sms`                                                                                                       |
| `altMessage` (String, Conditional)       | An alternate message for steps of type `linkedinSend`                                                                                                                                                                                                                                                                                         |
| `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`, `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`                                                                                                                                                                                                                                                   |
| `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                                                                                                                                                                      |
| `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)                                                                                                            |

## 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: []
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
                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 (required for email steps)
                message:
                  type: string
                  description: >-
                    Content of the email or message (used for email,
                    linkedinInvite, linkedinSend, manual, phone,
                    whatsappMessage, sms steps). Required for linkedinSend,
                    whatsappMessage, and sms
                altMessage:
                  type: string
                  description: Alternate message for linkedinSend steps
                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
                    - hasScore
                    - emailsOpened
                    - emailsClicked
                    - emailsUnsubscribed
                    - meetingBooked
                    - linkedinInviteAccepted
                    - linkedinOpened
                    - aircallDone
                    - linkedinNetworkCheck
                    - hasWhatsappAccount
                delayType:
                  type: string
                  description: Delay type for conditional steps
                  enum:
                    - within
                    - waitUntil
                campaignId:
                  type: string
                  description: >-
                    Target campaign ID for sendToAnotherCampaign steps. The
                    campaign must exist and not be archived
                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
              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
components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic

````