{
  "nodes": [
    {
      "id": "f64b448a-f0eb-4dd8-a05f-20711f8f1c26",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2304,
        -240
      ],
      "parameters": {
        "width": 588,
        "height": 852,
        "content": "## Send Personalized DMs to LinkedIn Profile Visitors\n\nThis workflow automatically identifies people who visited your LinkedIn profile and sends them personalized direct messages or connection requests based on their connection status.\n\n### Who is this for?\nSales professionals, recruiters, founders, and networkers who want to convert profile visitors into meaningful connections without manual outreach.\n\n### How it works\n1. **Fetch Visitors** \u2013 Retrieves your LinkedIn profile visitors from the past 7 days via ConnectSafely.ai API\n2. **Deduplicate** \u2013 Checks Google Sheets to avoid messaging the same person twice\n3. **Route by Connection** \u2013 Sends DMs to 1st-degree connections or connection requests to others\n4. **Track Progress** \u2013 Logs all outreach to Google Sheets for tracking\n\n### Setup steps\n1. Get your ConnectSafely.ai API key from [connectsafely.ai](https://connectsafely.ai)\n2. Configure the HTTP Bearer Auth credential with your API key\n3. Connect your Google Sheets account and create a sheet with columns: `Name`, `Linkedin URL`, `Status`\n4. Update the Google Sheets node with your sheet ID\n5. Customize the message templates in the Code nodes\n\n### Customization\n| What | Where |\n|------|-------|\n| Message templates | \"Generate DM for Connected User\" and \"Generate Message for New Connection\" Code nodes |\n| Fetch frequency | Schedule Trigger node |\n| Time range for visitors | \"Fetch Profile Visitors\" HTTP Request body |\n\n**Note:** This template requires a ConnectSafely.ai account and API access."
      },
      "typeVersion": 1
    },
    {
      "id": "a0c192b8-e394-4ba1-ab5a-af47abdf6937",
      "name": "Section 1 - Fetch Visitors",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1686,
        84
      ],
      "parameters": {
        "color": 7,
        "width": 460,
        "height": 284,
        "content": "## 1. Fetch & Split Visitors\nRetrieves profile visitors and prepares them for individual processing."
      },
      "typeVersion": 1
    },
    {
      "id": "3a053157-fec1-465e-9154-8189edf61c96",
      "name": "Section 2 - Deduplicate",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 316,
        "content": "## 2. Deduplicate\nChecks if visitor was already contacted to avoid duplicate outreach."
      },
      "typeVersion": 1
    },
    {
      "id": "ae2c1f44-4f82-42fc-bfc5-4243d477f9c8",
      "name": "Section 3 - Send Messages",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 844,
        "height": 532,
        "content": "## 3. Route & Send Messages\nSends DMs to connected users or connection requests to new prospects."
      },
      "typeVersion": 1
    },
    {
      "id": "d859a78a-66dd-47b4-ba6a-2471c96aee86",
      "name": "Section 4 - Track Progress",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 532,
        "content": "## 4. Track & Loop\nLogs outreach to Google Sheets and processes next visitor."
      },
      "typeVersion": 1
    },
    {
      "id": "10ccda8b-2c59-48ea-a2ba-a50725c4f198",
      "name": "Extract Profile ID from URL",
      "type": "n8n-nodes-base.code",
      "position": [
        -272,
        64
      ],
      "parameters": {
        "jsCode": "const url = $('Loop Over Items').first().json.navigationUrl\n\nlet profile_id = null;\n\nif (url) {\n  // Remove query params and trailing slash\n  const cleanUrl = url.split('?')[0].replace(/\\/$/, '');\n\n  // Extract profile ID after /in/\n  const match = cleanUrl.match(/linkedin\\.com\\/in\\/([^/]+)/);\n\n  if (match) {\n    profile_id = match[1];\n  }\n}\n\nreturn {\n  profile_id,\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "0d693106-d391-4c15-8eee-c878b5c24e3c",
      "name": "Fetch Profile Visitors",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1392,
        208
      ],
      "parameters": {
        "url": "https://api.connectsafely.ai/linkedin/profile/visitors",
        "method": "POST",
        "options": {},
        "jsonBody": "{\"timeRange\":\"past_7_days\",\"start\":0,\"fetchAll\":true}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7000a0b8-3431-4fca-b696-2c4fee4d0aeb",
      "name": "Weekly Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1616,
        208
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6a27975a-28b9-4e9d-8cb3-f9c1731b735e",
      "name": "Send Connection Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        160
      ],
      "parameters": {
        "url": "https://api.connectsafely.ai/linkedin/connect",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "profileId",
              "value": "={{ $('Extract Profile ID from URL').item.json.profile_id }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "22cb5dc4-555d-4cba-8599-d4d128e20180",
      "name": "Check if 1st Degree Connection",
      "type": "n8n-nodes-base.if",
      "position": [
        -48,
        64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0cca670d-4ced-4027-98f4-9337048d8a1d",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Loop Over Items').item.json.connectionDegree }}",
              "rightValue": "1st"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2fedafe7-567e-4cdc-83db-7da8ce3a9fa6",
      "name": "Send DM to Connected User",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        -32
      ],
      "parameters": {
        "url": "https://api.connectsafely.ai/linkedin/message",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "recipientProfileId",
              "value": "={{ $('Extract Profile ID from URL').item.json.profile_id }}"
            },
            {
              "name": "message",
              "value": "={{ $json.message }}"
            },
            {
              "name": "messageType",
              "value": "inmail"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "af60a93f-7b21-4187-a243-93c648b0f348",
      "name": "Generate Message for New Connection",
      "type": "n8n-nodes-base.code",
      "position": [
        176,
        160
      ],
      "parameters": {
        "jsCode": "// Get recipient name\nconst rawName = $('Loop Over Items').first().json.name || 'there';\nconst name = rawName.trim();\n\n// Sender footer - CUSTOMIZE THIS\nconst footer = `\\n\\n\u2014 Your Name`;\n\n// Message bodies for NON-connected users (connection requests)\n// CUSTOMIZE THESE MESSAGES for your use case\nconst messages = [\n  \"Noticed you checked out my profile, so I thought I'd reach out and connect. I'm working on [YOUR PRODUCT] \u2014 [BRIEF DESCRIPTION]. Happy to connect and share ideas.\",\n\n  \"Saw that you viewed my profile \u2014 always great to connect with like-minded professionals. I'm building [YOUR PRODUCT] to help with [VALUE PROP]. Would love to stay in touch.\",\n\n  \"Thanks for stopping by my profile! I'm currently working on [YOUR PRODUCT], focused on [BENEFIT]. Let's connect and exchange insights.\",\n\n  \"Noticed your visit on my profile and thought I'd say hello. I'm building [YOUR PRODUCT] to help [TARGET AUDIENCE]. Happy to connect!\",\n\n  \"Hey there! I saw you viewed my profile and wanted to connect. I'm working on [YOUR PRODUCT] \u2014 [BRIEF VALUE]. Looking forward to connecting.\"\n];\n\n// Pick random body\nconst body = messages[Math.floor(Math.random() * messages.length)];\n\n// FINAL MESSAGE\nconst finalMessage = `Hey ${name},\\n\\n${body}${footer}`;\n\nreturn {\n  message: finalMessage,\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a80ac098-2ff0-456b-8675-151fc9c0c170",
      "name": "Generate DM for Connected User",
      "type": "n8n-nodes-base.code",
      "position": [
        176,
        -32
      ],
      "parameters": {
        "jsCode": "// Get recipient name\nconst rawName = $('Loop Over Items').first().json.name || 'there';\nconst name = rawName.trim();\n\n// Sender footer - CUSTOMIZE THIS\nconst footer = `\\n\\n\u2014 Your Name`;\n\n// Message bodies for CONNECTED users (direct messages)\n// CUSTOMIZE THESE MESSAGES for your use case\nconst messages = [\n  \"Noticed you recently checked out my profile, so I thought I'd reach out. I've been working on [YOUR PRODUCT] \u2014 [BRIEF DESCRIPTION]. Happy to share if it's relevant.\",\n\n  \"Saw your visit on my profile and wanted to say hello. I'm currently building [YOUR PRODUCT] to help professionals [VALUE PROP].\",\n\n  \"I noticed you viewed my profile, so I thought I'd connect the dots. I've been working on [YOUR PRODUCT], focused on [BENEFIT].\",\n\n  \"Thanks for stopping by my profile! I'm building [YOUR PRODUCT] to help [TARGET AUDIENCE]. Happy to chat if useful.\",\n\n  \"Noticed your profile visit and thought I'd reach out. I'm working on [YOUR PRODUCT] \u2014 [BRIEF VALUE PROPOSITION].\"\n];\n\n// Pick random body\nconst body = messages[Math.floor(Math.random() * messages.length)];\n\n// FINAL MESSAGE\nconst finalMessage = `Hey ${name},\\n\\n${body}${footer}`;\n\nreturn {\n  message: finalMessage,\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f472a968-d6c3-4a94-a323-3bd80c31eaad",
      "name": "Split Visitors Array",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -1168,
        208
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "visitors"
      },
      "typeVersion": 1
    },
    {
      "id": "0fec9210-7b84-4618-990c-a73462a32be5",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -944,
        208
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "f32e7660-6f6f-4566-ae5d-9da2353f5fe0",
      "name": "Continue to Next Visitor",
      "type": "n8n-nodes-base.noOp",
      "position": [
        848,
        64
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "2a92195e-9b9f-4613-8111-8b3c046f18a8",
      "name": "Wait Between Messages",
      "type": "n8n-nodes-base.wait",
      "position": [
        1072,
        304
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "04b34961-4e22-4d7a-85b4-d8f4e2cb78e5",
      "name": "Log DM Sent to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        624,
        -32
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $('Loop Over Items').item.json.name }}",
            "Status": "DONE",
            "Linkedin URL": "={{ $('Loop Over Items').item.json.navigationUrl }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Linkedin URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Linkedin URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Linkedin URL"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "80964e3b-aac9-4950-8cf5-99378edba2e2",
      "name": "Check if Already Contacted",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -720,
        136
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.navigationUrl }}",
              "lookupColumn": "Linkedin URL"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "61c6dbdc-97f1-4bae-8cae-baf823f63803",
      "name": "Skip if Already Contacted",
      "type": "n8n-nodes-base.if",
      "position": [
        -496,
        136
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "149e3e45-fe35-4ef5-bbdd-da8107b82802",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $json.isEmpty() }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "23d345d5-df7c-41f2-a181-7b731b631a50",
      "name": "Log Connection Request to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        624,
        160
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $('Loop Over Items').item.json.name }}",
            "Status": "DONE",
            "Linkedin URL": "={{ $('Loop Over Items').item.json.navigationUrl }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Linkedin URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Linkedin URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Linkedin URL"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.7
    }
  ],
  "connections": {
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Check if Already Contacted",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log DM Sent to Sheet": {
      "main": [
        [
          {
            "node": "Continue to Next Visitor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Visitors Array": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Between Messages": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Profile Visitors": {
      "main": [
        [
          {
            "node": "Split Visitors Array",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Connection Request": {
      "main": [
        [
          {
            "node": "Log Connection Request to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Schedule Trigger": {
      "main": [
        [
          {
            "node": "Fetch Profile Visitors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Continue to Next Visitor": {
      "main": [
        [
          {
            "node": "Wait Between Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send DM to Connected User": {
      "main": [
        [
          {
            "node": "Log DM Sent to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip if Already Contacted": {
      "main": [
        [
          {
            "node": "Extract Profile ID from URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait Between Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Already Contacted": {
      "main": [
        [
          {
            "node": "Skip if Already Contacted",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Profile ID from URL": {
      "main": [
        [
          {
            "node": "Check if 1st Degree Connection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if 1st Degree Connection": {
      "main": [
        [
          {
            "node": "Generate DM for Connected User",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate Message for New Connection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate DM for Connected User": {
      "main": [
        [
          {
            "node": "Send DM to Connected User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Connection Request to Sheet": {
      "main": [
        [
          {
            "node": "Continue to Next Visitor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Message for New Connection": {
      "main": [
        [
          {
            "node": "Send Connection Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}