L

LinkedIn Outreach Platform Agentic Workflow

Validate All Applicants — LinkedIn Outreach Platform Automation Workflow

Two-pass validation: warms up candidates first (triggers security scans), then validates. Handles 1-25 candidates dynamically.

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

Two-pass validation: warms up candidates first (triggers security scans), then validates. Handles 1-25 candidates dynamically.

linkedin_validate_applicants.yaml

Workflow Definition

YAML source for the linkedin_validate_applicants.yaml workflow. This is the complete definition executed by the SideButton MCP server when LinkedIn Outreach Platform agents run this automation.

schema_version: 1
version: "2.1.0"
last_verified: "2026-02-27"
id: linkedin_validate_applicants
title: "Validate All Applicants"
description: "Two-pass validation: warms up candidates first (triggers security scans), then validates. Handles 1-25 candidates dynamically."
category:
  level: task
  domain: hiring
  reusable: true
params:
  job_summary:
    type: string
    description: |
      Optional: Pre-defined job requirements summary. If provided, skips extraction.
      Include JOB_ID in first line for tracking: "JOB_ID: 4350796385\n..."
      Add to settings.json user_contexts for persistence.
    required: false
policies:
  allowed_domains:
    - "*.linkedin.com"
embed:
  selector: "button[aria-label^='Manage job']"
  position: after
  when: ".hiring-applicants__list-item"
  label: "AI Validate All"
  result:
    action: "popup"
steps:
  # ================================================================
  # VISUAL FEEDBACK: Inject progress styles for candidate rows
  # ================================================================

  - type: browser.injectCSS
    id: sb-batch-validate
    css: |
      @keyframes sb-pulse-border {
        0%, 100% { border-left-color: rgba(21, 195, 154, 0.3); }
        50% { border-left-color: rgba(21, 195, 154, 1); }
      }
      .hiring-applicants__list-item.sb-processing {
        border-left: 4px solid #15C39A !important;
        animation: sb-pulse-border 1.2s ease-in-out infinite !important;
        background: rgba(21, 195, 154, 0.04) !important;
        transition: all 0.3s ease !important;
      }
      .hiring-applicants__list-item.sb-done {
        border-left: 4px solid #22C55E !important;
        animation: none !important;
        background: rgba(34, 197, 94, 0.04) !important;
        transition: all 0.3s ease !important;
      }
      .hiring-applicants__list-item.sb-pending {
        opacity: 0.5 !important;
        transition: opacity 0.3s ease !important;
      }
      #sb-progress-banner {
        position: sticky;
        top: 0;
        z-index: 100;
        background: linear-gradient(135deg, #15C39A 0%, #0EA87D 100%);
        color: white;
        padding: 8px 16px;
        font-size: 13px;
        font-weight: 600;
        display: flex;
        align-items: center;
        gap: 8px;
        border-radius: 6px;
        margin-bottom: 8px;
      }
      #sb-progress-banner .sb-spinner {
        width: 14px; height: 14px;
        border: 2px solid rgba(255,255,255,0.3);
        border-top-color: white;
        border-radius: 50%;
        animation: sb-spin 0.8s linear infinite;
      }
      @keyframes sb-spin {
        to { transform: rotate(360deg); }
      }

  # Inject progress banner above the applicant list
  - type: browser.injectJS
    js: |
      const list = document.querySelector('.hiring-applicants__list');
      if (list && !document.getElementById('sb-progress-banner')) {
        const banner = document.createElement('div');
        banner.id = 'sb-progress-banner';
        banner.innerHTML = '<div class="sb-spinner"></div><span>Preparing validation...</span>';
        list.parentElement.insertBefore(banner, list);
      }

  # ================================================================
  # PHASE 1: GET JOB REQUIREMENTS
  # ================================================================

  - type: llm.generate
    prompt: |
      Extract the numeric job ID from this LinkedIn hiring URL:
      {{_url}}

      Example: https://www.linkedin.com/hiring/jobs/4350796385/applicants/12345/detail/
      Returns: 4350796385

      Return ONLY the numeric job ID, nothing else.
    as: job_id

  - type: control.if
    condition: "{{job_summary}} == ''"
    then:
      - type: workflow.call
        workflow: linkedin_extract_job_requirements
        params:
          job_id: "{{job_id}}"
        as: job_info

      - type: variable.set
        name: effective_job_summary
        value: "{{job_info.job_summary}}"

    else:
      - type: variable.set
        name: effective_job_summary
        value: "{{job_summary}}"

  - type: control.if
    condition: "{{effective_job_summary}} == ''"
    then:
      - type: control.stop
        message: "ERROR: No job requirements for job {{job_id}}. Provide job_summary param or ensure extraction succeeds."

  # ================================================================
  # PHASE 2: RETURN TO APPLICANTS PAGE (only if we navigated away)
  # ================================================================

  - type: control.if
    condition: "{{job_summary}} == ''"
    then:
      - type: browser.navigate
        url: "{{_url}}"

  - type: browser.wait
    selector: ".hiring-applicants__list-item"
    timeout: 10000

  - type: browser.wait
    ms: 2000

  # ================================================================
  # PHASE 3: COUNT CANDIDATES ON PAGE
  # ================================================================

  # Extract text from all applicant list items to count them
  - type: browser.extract
    selector: ".hiring-applicants__list-item"
    as: applicant_list_content

  - type: llm.generate
    prompt: |
      Count how many individual applicant entries appear in this content.
      Each applicant has a name and job title.

      Content:
      {{applicant_list_content}}

      Return ONLY a number between 1 and 25, nothing else.
      If you cannot determine, return 25.
      Example: 17
    as: candidate_count

  # ================================================================
  # PHASE 4: WARM-UP ALL CANDIDATES (Trigger Security Scans)
  # ================================================================

  # Update banner and dim pending candidates
  - type: browser.injectJS
    js: |
      const banner = document.getElementById('sb-progress-banner');
      if (banner) banner.querySelector('span').textContent = 'Warming up candidates (0/{{candidate_count}})...';
      document.querySelectorAll('.hiring-applicants__list-item').forEach(el => el.classList.add('sb-pending'));

  # Warm-up 1 (always)
  - type: workflow.call
    workflow: linkedin_warmup_candidate
    params:
      index: "1"

  # Warm-up 2
  - type: control.if
    condition: "{{candidate_count}} >= 2"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "2"

  # Warm-up 3
  - type: control.if
    condition: "{{candidate_count}} >= 3"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "3"

  # Warm-up 4
  - type: control.if
    condition: "{{candidate_count}} >= 4"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "4"

  # Warm-up 5
  - type: control.if
    condition: "{{candidate_count}} >= 5"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "5"

  # Warm-up 6
  - type: control.if
    condition: "{{candidate_count}} >= 6"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "6"

  # Warm-up 7
  - type: control.if
    condition: "{{candidate_count}} >= 7"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "7"

  # Warm-up 8
  - type: control.if
    condition: "{{candidate_count}} >= 8"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "8"

  # Warm-up 9
  - type: control.if
    condition: "{{candidate_count}} >= 9"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "9"

  # Warm-up 10
  - type: control.if
    condition: "{{candidate_count}} >= 10"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "10"

  # Warm-up 11
  - type: control.if
    condition: "{{candidate_count}} >= 11"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "11"

  # Warm-up 12
  - type: control.if
    condition: "{{candidate_count}} >= 12"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "12"

  # Warm-up 13
  - type: control.if
    condition: "{{candidate_count}} >= 13"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "13"

  # Warm-up 14
  - type: control.if
    condition: "{{candidate_count}} >= 14"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "14"

  # Warm-up 15
  - type: control.if
    condition: "{{candidate_count}} >= 15"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "15"

  # Warm-up 16
  - type: control.if
    condition: "{{candidate_count}} >= 16"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "16"

  # Warm-up 17
  - type: control.if
    condition: "{{candidate_count}} >= 17"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "17"

  # Warm-up 18
  - type: control.if
    condition: "{{candidate_count}} >= 18"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "18"

  # Warm-up 19
  - type: control.if
    condition: "{{candidate_count}} >= 19"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "19"

  # Warm-up 20
  - type: control.if
    condition: "{{candidate_count}} >= 20"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "20"

  # Warm-up 21
  - type: control.if
    condition: "{{candidate_count}} >= 21"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "21"

  # Warm-up 22
  - type: control.if
    condition: "{{candidate_count}} >= 22"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "22"

  # Warm-up 23
  - type: control.if
    condition: "{{candidate_count}} >= 23"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "23"

  # Warm-up 24
  - type: control.if
    condition: "{{candidate_count}} >= 24"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "24"

  # Warm-up 25
  - type: control.if
    condition: "{{candidate_count}} >= 25"
    then:
      - type: workflow.call
        workflow: linkedin_warmup_candidate
        params:
          index: "25"

  # ================================================================
  # PHASE 5: RESET TO FIRST CANDIDATE
  # ================================================================

  - type: browser.wait
    ms: 1000

  # Click first candidate to reset view (this also scrolls it into view)
  - type: browser.click
    selector: ".hiring-applicants__list-item:nth-child(1) a"

  - type: browser.wait
    ms: 1500

  # ================================================================
  # PHASE 6: VALIDATE APPLICANTS (Conditional based on count)
  # ================================================================

  # Update banner for validation phase
  - type: browser.injectJS
    js: |
      const banner = document.getElementById('sb-progress-banner');
      if (banner) banner.querySelector('span').textContent = 'Validating candidates (0/{{candidate_count}})...';
      document.querySelectorAll('.hiring-applicants__list-item').forEach(el => {
        el.classList.remove('sb-pending');
      });

  # Validate 1 (always)
  - type: workflow.call
    workflow: linkedin_validate_single_applicant
    params:
      index: "1"
      job_summary: "{{effective_job_summary}}"
    as: result_1

  # Validate 2
  - type: control.if
    condition: "{{candidate_count}} >= 2"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "2"
          job_summary: "{{effective_job_summary}}"
        as: result_2

  # Validate 3
  - type: control.if
    condition: "{{candidate_count}} >= 3"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "3"
          job_summary: "{{effective_job_summary}}"
        as: result_3

  # Validate 4
  - type: control.if
    condition: "{{candidate_count}} >= 4"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "4"
          job_summary: "{{effective_job_summary}}"
        as: result_4

  # Validate 5
  - type: control.if
    condition: "{{candidate_count}} >= 5"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "5"
          job_summary: "{{effective_job_summary}}"
        as: result_5

  # Validate 6
  - type: control.if
    condition: "{{candidate_count}} >= 6"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "6"
          job_summary: "{{effective_job_summary}}"
        as: result_6

  # Validate 7
  - type: control.if
    condition: "{{candidate_count}} >= 7"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "7"
          job_summary: "{{effective_job_summary}}"
        as: result_7

  # Validate 8
  - type: control.if
    condition: "{{candidate_count}} >= 8"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "8"
          job_summary: "{{effective_job_summary}}"
        as: result_8

  # Validate 9
  - type: control.if
    condition: "{{candidate_count}} >= 9"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "9"
          job_summary: "{{effective_job_summary}}"
        as: result_9

  # Validate 10
  - type: control.if
    condition: "{{candidate_count}} >= 10"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "10"
          job_summary: "{{effective_job_summary}}"
        as: result_10

  # Validate 11
  - type: control.if
    condition: "{{candidate_count}} >= 11"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "11"
          job_summary: "{{effective_job_summary}}"
        as: result_11

  # Validate 12
  - type: control.if
    condition: "{{candidate_count}} >= 12"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "12"
          job_summary: "{{effective_job_summary}}"
        as: result_12

  # Validate 13
  - type: control.if
    condition: "{{candidate_count}} >= 13"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "13"
          job_summary: "{{effective_job_summary}}"
        as: result_13

  # Validate 14
  - type: control.if
    condition: "{{candidate_count}} >= 14"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "14"
          job_summary: "{{effective_job_summary}}"
        as: result_14

  # Validate 15
  - type: control.if
    condition: "{{candidate_count}} >= 15"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "15"
          job_summary: "{{effective_job_summary}}"
        as: result_15

  # Validate 16
  - type: control.if
    condition: "{{candidate_count}} >= 16"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "16"
          job_summary: "{{effective_job_summary}}"
        as: result_16

  # Validate 17
  - type: control.if
    condition: "{{candidate_count}} >= 17"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "17"
          job_summary: "{{effective_job_summary}}"
        as: result_17

  # Validate 18
  - type: control.if
    condition: "{{candidate_count}} >= 18"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "18"
          job_summary: "{{effective_job_summary}}"
        as: result_18

  # Validate 19
  - type: control.if
    condition: "{{candidate_count}} >= 19"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "19"
          job_summary: "{{effective_job_summary}}"
        as: result_19

  # Validate 20
  - type: control.if
    condition: "{{candidate_count}} >= 20"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "20"
          job_summary: "{{effective_job_summary}}"
        as: result_20

  # Validate 21
  - type: control.if
    condition: "{{candidate_count}} >= 21"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "21"
          job_summary: "{{effective_job_summary}}"
        as: result_21

  # Validate 22
  - type: control.if
    condition: "{{candidate_count}} >= 22"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "22"
          job_summary: "{{effective_job_summary}}"
        as: result_22

  # Validate 23
  - type: control.if
    condition: "{{candidate_count}} >= 23"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "23"
          job_summary: "{{effective_job_summary}}"
        as: result_23

  # Validate 24
  - type: control.if
    condition: "{{candidate_count}} >= 24"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "24"
          job_summary: "{{effective_job_summary}}"
        as: result_24

  # Validate 25
  - type: control.if
    condition: "{{candidate_count}} >= 25"
    then:
      - type: workflow.call
        workflow: linkedin_validate_single_applicant
        params:
          index: "25"
          job_summary: "{{effective_job_summary}}"
        as: result_25

  # ================================================================
  # PHASE 7: COMPLETION SUMMARY
  # ================================================================

  # Update banner to complete state
  - type: browser.injectJS
    js: |
      const banner = document.getElementById('sb-progress-banner');
      if (banner) {
        banner.style.background = 'linear-gradient(135deg, #22C55E 0%, #16A34A 100%)';
        banner.innerHTML = '<span>Validation complete — generating summary...</span>';
      }

  - type: llm.generate
    prompt: |
      Summarize the validation results. Count only non-empty results.
      Total candidates on page: {{candidate_count}}

      Results (some may be empty if fewer than 25 candidates):
      1: {{result_1.rating}}
      2: {{result_2.rating}}
      3: {{result_3.rating}}
      4: {{result_4.rating}}
      5: {{result_5.rating}}
      6: {{result_6.rating}}
      7: {{result_7.rating}}
      8: {{result_8.rating}}
      9: {{result_9.rating}}
      10: {{result_10.rating}}
      11: {{result_11.rating}}
      12: {{result_12.rating}}
      13: {{result_13.rating}}
      14: {{result_14.rating}}
      15: {{result_15.rating}}
      16: {{result_16.rating}}
      17: {{result_17.rating}}
      18: {{result_18.rating}}
      19: {{result_19.rating}}
      20: {{result_20.rating}}
      21: {{result_21.rating}}
      22: {{result_22.rating}}
      23: {{result_23.rating}}
      24: {{result_24.rating}}
      25: {{result_25.rating}}

      Count how many are in each category (ignore empty/undefined):

      VALIDATION COMPLETE
      ==================
      Good Fit: X applicants
      Maybe: X applicants
      Not a Fit: X applicants
      ==================
      Total Processed: {{candidate_count}}
    as: summary

  - type: control.stop
    message: "{{summary}}"

How To Run

Install the LinkedIn Outreach Platform knowledge pack into your SideButton agent, then dispatch this workflow by its ID linkedin_validate_applicants.yaml. Agents invoke it directly via the MCP protocol or through the portal.