L

LinkedIn Outreach Platform Agentic Workflow

Draft Custom Message — LinkedIn Outreach Platform Agentic Workflow

Finds a person's thread via inbox search, clears the compose box, and types caller-supplied message text into it — draft only, never sends

Available free v1.1.0 Browser LLM
$ sidebutton install linkedin
Download ZIP

The mechanical half of outreach: given final message text, put it into the right compose box and stop. Pairs with linkedin_thread_history and linkedin_profile_brief (research) or takes text written by hand.

Finds the thread through message search (survives the Focused/Other inbox split), clears any previous draft via an in-page select-all + delete (LinkedIn's compose is a React contenteditable — setting textContent directly desyncs its editor state, and browser.fill does not work on it), then types the text.

This workflow never sends. LinkedIn's compose has press-Enter-to-send enabled by default, so the text is typed without any trailing Enter; a human reviews the box and presses Enter themselves.

Boundary: requires an existing conversation with the person (any 1st-degree contact who ever exchanged a message, including connection notes). For a contact with no thread at all, the profile-page Message overlay renders inside an iframe this schema cannot reach — draft manually there.

Steps

  1. 1.
    Navigate to a URL
    url
    https://www.linkedin.com/messaging/?searchTerm={{person_name}}
    browser.navigate
  2. 2.
    Wait
    selector
    main
    timeout
    12000
    browser.wait
  3. 3.
    Wait
    ms
    1500
    browser.wait
  4. 4.
    Click an element
    selector
    li[class*="conversation"]:has-text("{{person_name}}")
    browser.click
  5. 5.
    Wait
    selector
    .msg-form__contenteditable
    timeout
    8000
    browser.wait
  6. 6.
    Click an element
    selector
    .msg-form__contenteditable
    browser.click
  7. 7.
    browser injectJS
    id
    sb-linkedin-clear-compose
    as
    clear_result
    js
    |
    browser.injectJS
  8. 8.
    Type into a field
    selector
    .msg-form__contenteditable
    text
    {{message_text}}
    browser.type
  9. 9.
    Wait
    ms
    300
    browser.wait
  10. 10.
    control stop
    message
    Draft typed into the thread with {{person_name}} — NOT sent. Review the compose box and press Enter to send. (Compose clear step reported: {{clear_result}})
    control.stop

Workflow definition

schema_version: 1
version: "1.0.0"
last_verified: "2026-08-25"
id: linkedin_draft_outreach
title: "Draft Custom Message"
description: "Finds a person's thread via inbox search, clears the compose box, and types caller-supplied message text into it — draft only, never sends"
overview: |
  The mechanical half of outreach: given final message text, put it into the right
  compose box and stop. Pairs with `linkedin_thread_history` and
  `linkedin_profile_brief` (research) or takes text written by hand.

  Finds the thread through message search (survives the Focused/Other inbox split),
  clears any previous draft via an in-page select-all + delete (LinkedIn's compose is a
  React contenteditable — setting textContent directly desyncs its editor state, and
  `browser.fill` does not work on it), then types the text.

  **This workflow never sends.** LinkedIn's compose has press-Enter-to-send enabled by
  default, so the text is typed without any trailing Enter; a human reviews the box and
  presses Enter themselves.

  Boundary: requires an existing conversation with the person (any 1st-degree contact
  who ever exchanged a message, including connection notes). For a contact with no
  thread at all, the profile-page Message overlay renders inside an iframe this schema
  cannot reach — draft manually there.
category:
  level: task
  domain: sales
  reusable: true
params:
  person_name:
    type: string
    description: Name as shown in the inbox, used for search and thread selection
    required: true
  message_text:
    type: string
    description: Final message text to place in the compose box, verbatim
    required: true
policies:
  allowed_domains:
    - "*.linkedin.com"
steps:
  - type: browser.navigate
    url: https://www.linkedin.com/messaging/?searchTerm={{person_name}}
  - type: browser.wait
    selector: main
    timeout: 12000
  - type: browser.wait
    ms: 1500
  - type: browser.click
    selector: 'li[class*="conversation"]:has-text("{{person_name}}")'
  - type: browser.wait
    selector: .msg-form__contenteditable
    timeout: 8000
  - type: browser.click
    selector: .msg-form__contenteditable
  - type: browser.injectJS
    id: sb-linkedin-clear-compose
    as: clear_result
    js: |
      (() => {
        const box = document.querySelector('.msg-form__contenteditable');
        if (!box) return 'compose box not found';
        box.focus();
        document.execCommand('selectAll', false, null);
        document.execCommand('delete', false, null);
        return 'cleared';
      })();
  - type: browser.type
    selector: .msg-form__contenteditable
    text: "{{message_text}}"
  - type: browser.wait
    ms: 300
  - type: control.stop
    message: "Draft typed into the thread with {{person_name}} — NOT sent. Review the compose box and press Enter to send. (Compose clear step reported: {{clear_result}})"