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

> Updates an existing step within a sequence.

# Update Sequence Step

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="Step" objectPath="step" />

Use this endpoint to:

* Edit the message content or subject of the step
* Change the delay before this step executes
* Update the title (for manual steps)
* Change method/URL (for API steps)
* Adjust delay logic for conditional steps

<Warning>
  You **cannot** change the `type` of a step (e.g., from `email` to `linkedinInvite`). To do so, delete the step and create a new one.
  However, you **need** to add the `type` field in the request body even if you can't modify it...
</Warning>

<Warning>
  This endpoint refuses any condition step that has **more than one branch** besides Else, with
  `400 Multi-branch conditions cannot be edited via the API yet.` — its rebuild would collapse the step
  back to a single branch plus Else and silently drop the others.

  To change the condition key of such a step, remove branches with
  [`DELETE …/branches/{branchSequenceId}`](/api-reference/endpoints/sequences/delete-condition-branch)
  until one is left, then patch it here. To change what one branch tests without touching the others,
  use [`PATCH …/branches/{branchSequenceId}`](/api-reference/endpoints/sequences/update-condition-branch)
  instead.
</Warning>

## Running campaigns

Editing a step does not require pausing the campaign. The new content applies to the sends that
follow: a lead who already received that step keeps the version that was sent to them, and a lead
who has not reached it yet gets the edited one.

A refusal carries a `code` in the body next to the message, so a rejected edit can be told apart
from a transport failure.

## Supported Step Types

| Type | Editable Fields | Description |
| - | - | - |
| `email` | `subject`, `message`, `delay` | Sends an email to the lead |
| `manual` | `title`, `message`, `delay` | Manual task to be performed by the user |
| `phone` | `message`, `delay` | Reminder to make a phone call |
| `api` | `method`, `url`, `delay` | Makes an API call |
| `linkedinVisit` | `delay` | Simulates a profile visit on LinkedIn |
| `linkedinInvite` | `message`, `altMessage`, `images`, `videos`, `delay` | Sends a LinkedIn invite with message |
| `linkedinSend` | `message`, `altMessage`, `altMessagePremium`, `images`, `videos`, `delay` | Sends a LinkedIn message |
| `linkedinVoiceNote` | `message`, `altMessage`, `delay`, `recordMode` | Sends a LinkedIn voice note. Audio is either recorded manually or AI-generated from a text template, depending on `recordMode` |
| `linkedinFollow` | `delay` | Follows the lead on LinkedIn |
| `linkedinLikeLastPost` | `delay` | Likes the lead's most recent LinkedIn post |
| `linkedinCommentLastPost` | `delay` | Posts an AI-generated comment on the lead's most recent LinkedIn post |
| `linkedinEndorse` | `delay`, `skillName`, `endorseAnyFallback` | Endorses a skill on the lead's LinkedIn profile |
| `linkedinWithdrawInvitation` | `delay` | Withdraws a pending LinkedIn invite |
| `sendToAnotherCampaign` | `campaignId` | Sends the lead to another campaign |
| `conditional` | `conditionKey`, `delayType`, `delay`, `customField`, `customOperator`, `customValue` | Triggers the next step based on lead behavior |
| `whatsappMessage` | `message`, `delay` | Sends a WhatsApp message |
| `sms` | `message`, `delay` | Sends an SMS to the lead |

## Request Body

Only include the fields you want to update. Required fields are not enforced here.

| Field | Type | Applies To | Description |
| - | - | - | - |
| `subject` | string | `email` | Email subject line. Maximum 400 characters (applied to the raw template, including any Liquid syntax) |
| `message` | string | `email`, `linkedin*`, `manual`, `phone`, `whatsappMessage`, `sms` | Message text |
| `altMessage` | string | `linkedinInvite`, `linkedinSend`, `linkedinVoiceNote` | The premium invitation note on an invite, the out-of-network note on the other two. Pass an empty string to clear it. See [LinkedIn invitation notes](/api-reference/endpoints/sequences/add-step-to-sequence#linkedin-invitation-notes) on Add Step |
| `altMessagePremium` | string | `linkedinSend` | The premium variant of the out-of-network note. Pass an empty string to clear it. See [LinkedIn invitation notes](/api-reference/endpoints/sequences/add-step-to-sequence#linkedin-invitation-notes) on Add Step |
| `title` | string | `manual` | Title for manual task. Maximum 400 characters |
| `method` | string | `api` | One of `GET`, `POST`, `PUT`, `DELETE`, `PATCH` |
| `url` | string | `api` | Valid HTTP/HTTPS URL |
| `delay` | integer | all except `sendToAnotherCampaign` | Delay before this step, in days. Must be between 0 and 1500 |
| `delayType` | string | `conditional` | One of `within`, `waitUntil` |
| `conditionKey` | string | `conditional` | See supported condition keys below |
| `customField` | string | `conditional` keyed `customLeadInfo` | The field to test. A bare name reads as a lead variable (`jobTitle` becomes `variables.jobTitle`); prefix with `fields.` for a contact field |
| `customOperator` | string | `conditional` keyed `customLeadInfo` | One of `equal`, `contains`, `empty`, `notEmpty` |
| `customValue` | string | `conditional` keyed `customLeadInfo` | The value the field is compared to. Required for `equal` and `contains`, refused for `empty` and `notEmpty` |
| `campaignId` | string | `sendToAnotherCampaign` | ID of destination campaign |
| `leadAction` | string | `sendToAnotherCampaign` | What happens to the lead in the source campaign once it has been moved. One of `continue`, `pause`, `stop`. See [What happens to the transferred lead](/api-reference/endpoints/sequences/add-step-to-sequence#what-happens-to-the-transferred-lead) on Add Step |
| `images` | array of strings | `linkedinInvite`, `linkedinSend` | Public HTTPS URLs of images to attach. Replaces the step's current images — pass `[]` to clear them. See the [LinkedIn media constraints](/api-reference/endpoints/sequences/add-step-to-sequence#linkedin-media-constraints) on Add Step |
| `videos` | array of strings | `linkedinInvite`, `linkedinSend` | Public HTTPS URLs of videos to attach. Replaces the step's current videos — pass `[]` to clear them. See the [LinkedIn media constraints](/api-reference/endpoints/sequences/add-step-to-sequence#linkedin-media-constraints) on Add Step |
| `skillName` | string | `linkedinEndorse` | Name of the LinkedIn skill to endorse on the lead's profile |
| `endorseAnyFallback` | boolean | `linkedinEndorse` | When `true` and the named skill is not on the lead's profile, lemlist falls back to endorsing any available skill |
| `recordMode` | string | `linkedinVoiceNote` | One of `manual` (user records the audio after step creation; default) or `ai` (audio is generated from a text template provided in the lemlist UI) |

<Note>
  To remove the subject of an `email` step, send `"subject": ""`. The email then replies in the thread
  of the previous email sent to the lead in this campaign instead of starting a new one.
</Note>

## Supported Condition Keys

For `conditional` steps:

* `hasEmailAddress`
* `hasLinkedinUrl`
* `hasPhoneNumber`
* `customLeadInfo`
* `hasScore`
* `emailsOpened`
* `emailsClicked`
* `emailsUnsubscribed`
* `meetingBooked`
* `linkedinInviteAccepted`
* `linkedinOpened`
* `aircallDone`
* `linkedinNetworkCheck`
* `hasWhatsappAccount`

A `customLeadInfo` step is patched with `customField` / `customOperator` / `customValue`; omitted
fields keep their stored value, so `{ "type": "conditional", "customValue": "CTO" }` changes the
tested value alone.


## OpenAPI

````yaml patch /sequences/{sequenceId}/steps/{stepId}
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/{stepId}:
    parameters:
      - name: sequenceId
        in: path
        required: true
        description: The unique identifier of the sequence
        example: seq_lkCSKd32qSZZTe5Ru
        schema:
          type: string
      - name: stepId
        in: path
        required: true
        description: The unique identifier of the step
        example: stp_V0JHmSpWiO0rxHUkz
        schema:
          type: string
    patch:
      tags:
        - Sequences
      summary: Update Sequence Step
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - type
              properties:
                type:
                  type: string
                  description: >-
                    The type of step (required but cannot be changed - must
                    match the existing step type)
                  enum:
                    - email
                    - manual
                    - phone
                    - api
                    - linkedinVisit
                    - linkedinInvite
                    - linkedinSend
                    - linkedinVoiceNote
                    - linkedinFollow
                    - linkedinLikeLastPost
                    - linkedinCommentLastPost
                    - linkedinEndorse
                    - linkedinWithdrawInvitation
                    - sendToAnotherCampaign
                    - conditional
                    - whatsappMessage
                    - sms
                delay:
                  type: integer
                  description: Delay in days before executing this step
                subject:
                  type: string
                  description: >-
                    Email subject line (for email steps). Pass an empty string
                    to remove it: the email then replies in the thread of the
                    previous email sent to the lead in this campaign.
                message:
                  type: string
                  description: >-
                    Content of the email or message (for email, linkedinInvite,
                    linkedinSend, manual, phone, whatsappMessage, sms steps, and
                    as the AI script of a linkedinVoiceNote step in `ai` record
                    mode)
                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. Pass an empty
                    string to clear it. 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). Pass an empty string to clear
                    it. 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 (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. Replaces the step's current images —
                    pass an empty array to clear them. 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. Replaces the step's current videos —
                    pass an empty array to clear them. 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:
              Update Email Step:
                value:
                  type: email
                  subject: '{{firstName}}, following up on our conversation'
                  message: >-
                    <p>Hi {{firstName}},</p><p>Just wanted to circle back on our
                    previous email. Have you had a chance to review
                    it?</p><p>Best,<br>{{senderName}}</p>
                  delay: 2
              Update LinkedIn Invite:
                value:
                  type: linkedinInvite
                  message: >-
                    Hi {{firstName}}, I'd love to connect with you to discuss
                    potential collaboration opportunities!
                  delay: 0
              Update LinkedIn Send:
                value:
                  type: linkedinSend
                  message: >-
                    Thanks for accepting my invite, {{firstName}}! I wanted to
                    reach out about...
                  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.
                  delay: 3
              Update LinkedIn Send Media:
                value:
                  type: linkedinSend
                  images:
                    - https://example.com/assets/overview.png
                  videos: []
              Update Manual Task:
                value:
                  type: manual
                  title: Schedule follow-up call
                  message: Discuss pain points and propose next steps
                  delay: 2
              Update Phone Call:
                value:
                  type: phone
                  message: Call to discuss the proposal sent via email
                  delay: 1
              Update API Call:
                value:
                  type: api
                  method: PUT
                  url: https://api.example.com/webhook/update
                  delay: 0
              Update LinkedIn Visit:
                value:
                  type: linkedinVisit
                  delay: 1
              Update Conditional Step:
                value:
                  type: conditional
                  conditionKey: emailsOpened
                  delayType: within
                  delay: 3
              Update Custom Lead Info Condition:
                value:
                  type: conditional
                  customOperator: equal
                  customValue: Founder
              Update Send to Another Campaign:
                value:
                  type: sendToAnotherCampaign
                  campaignId: cam_XYZ789ABC123
              Update WhatsApp Message:
                value:
                  type: whatsappMessage
                  message: >-
                    Hi {{firstName}}, just checking in to see if you received my
                    previous messages.
                  delay: 2
      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: 0
                emailTemplateId: etp_HZvLoLNEwdJ8Vu0w7
                message: Hello, I want to...
        '400':
          description: 'Possible errors: Bad team / Error from updateStep'
          content:
            text/plain:
              example: Bad team
        '401':
          description: The authentication you supplied is incorrect
          content:
            text/plain:
              example: The authentication you supplied is incorrect
        '402':
          description: >-
            `upper-plan-required` - the edited step or condition needs a higher
            plan or an add-on.
          content:
            application/json:
              example:
                error: Upgrade your plan to use this feature
                code: upper-plan-required
        '405':
          description: Method not allowed
        '409':
          description: >-
            The edit conflicts with what the campaign's leads are already
            running - for instance `incomplete-branch-on-sequence-in-use`, a
            condition branch left without a value to test.
          content:
            application/json:
              example:
                error: >-
                  Every branch of this condition needs a value once leads have
                  entered the campaign
                code: incomplete-branch-on-sequence-in-use
components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic

````

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