{
  "id": "WczQxQgtvjjk1HC1",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "LinkedIn Profile Enrichment With Error Handling",
  "tags": [],
  "nodes": [
    {
      "id": "node_0",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        160,
        80
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "node_1",
      "name": "Get Guests with LinkedIn",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        380,
        180
      ],
      "parameters": {
        "limit": 15,
        "table": "NOCODB_TABLEID",
        "options": {
          "where": "(LinkedIn,isnot,null)~and(linkedin_headline,is,null)"
        },
        "operation": "getAll",
        "projectId": "NOCODB_PROJECTID",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_2",
      "name": "Run Apify LinkedIn Scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        680,
        180
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/dev_fusion~linkedin-profile-scraper/runs",
        "method": "POST",
        "options": {},
        "jsonBody": "={\"profileUrls\": [\"{{$json.LinkedIn}}\"]}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth"
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "node_3",
      "name": "Wait for Completion",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        900,
        180
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/acts/dev_fusion~linkedin-profile-scraper/runs/{{$json.data.id}}",
        "options": {
          "timeout": 300000
        },
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "waitForFinish",
              "value": "240"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "node_4",
      "name": "Check Run Status",
      "type": "n8n-nodes-base.if",
      "position": [
        1120,
        180
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "93d97f85-c98a-4c0d-b7a4-e7c1c9a5e0a1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.data.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_5",
      "name": "Get Scraper Results",
      "type": "n8n-nodes-base.code",
      "onError": "continueErrorOutput",
      "position": [
        1340,
        80
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get the dataset ID from the previous node\nconst datasetId = $json.data.defaultDatasetId;\n\ntry {\n  // Get the Apify token\n  const apifyToken = '[YOUR APIFY TOKEN]';\n  \n  // Make the HTTP request to get the dataset\n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: `https://api.apify.com/v2/datasets/${datasetId}/items?token=YOUR_TOKEN_HERE\n    json: true\n  });\n\n  // Check if we have valid data\n  if (!Array.isArray(response) || response.length === 0) {\n    // No data found - this is a 404 profile\n    throw new Error('Empty dataset - LinkedIn profile not found or inaccessible');\n  }\n\n  // Check if the profile data is actually valid\n  const firstItem = response[0];\n  if (!firstItem || !firstItem.linkedinUrl || !firstItem.fullName) {\n    throw new Error('Invalid LinkedIn profile data');\n  }\n\n  // IMPORTANT: For runOnceForEachItem mode, we must return a single object\n  // n8n will handle the array data by flattening it\n  // We need to pass the array as a property of an object\n  return {\n    linkedinData: response\n  };\n} catch (error) {\n  // For HTTP errors (like 404 on dataset endpoint)\n  if (error.response && error.response.statusCode === 404) {\n    throw new Error('Dataset not found - LinkedIn profile not accessible');\n  }\n  // Re-throw other errors to route to error output\n  throw error;\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "node_7",
      "name": "Transform Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1640,
        -100
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Extract the LinkedIn data from the wrapped response\nconst dataArray = $json.linkedinData;\nif (!dataArray || !Array.isArray(dataArray) || dataArray.length === 0) {\n  throw new Error('No LinkedIn data found');\n}\n\n// Get the first (and should be only) item from the array\nconst linkedinData = dataArray[0];\n\n// Get the guest ID from the original NocoDB data\nlet guestId = null;\n\ntry {\n  guestId = $('Get Guests with LinkedIn').item.json.Id;\n} catch (error) {\n  throw new Error('Could not find guest ID. Error: ' + error.message);\n}\n\nif (!guestId) {\n  throw new Error('Guest ID is null or undefined');\n}\n\n// Get the current timestamp\nconst currentTimestamp = new Date().toISOString();\n\n// Transform the data to match our NocoDB fields\nconst transformedData = {\n  Id: guestId,\n  linkedin_url: linkedinData.linkedinUrl || '',\n  linkedin_full_name: linkedinData.fullName || '',\n  linkedin_first_name: linkedinData.firstName || '',\n  linkedin_headline: linkedinData.headline || '',\n  linkedin_email: linkedinData.email || '',\n  linkedin_bio: linkedinData.about || '',\n  linkedin_profile_pic: linkedinData.profilePicHighQuality || linkedinData.profilePic || '',\n  linkedin_current_role: linkedinData.jobTitle || '',\n  linkedin_current_company: linkedinData.companyName || '',\n  linkedin_country: linkedinData.addressCountryOnly || '',\n  linkedin_skills: linkedinData.topSkillsByEndorsements || '',\n  linkedin_company_website: linkedinData.companyWebsite || '',\n  linkedin_experiences: JSON.stringify(linkedinData.experiences || []),\n  linkedin_personal_website: JSON.stringify(linkedinData.creatorWebsite || {}),\n  linkedin_publications: JSON.stringify(linkedinData.publications || []),\n  linkedin_last_modified: currentTimestamp,\n  linkedin_scrape_status: 'success',\n  linkedin_scrape_last_attempt: currentTimestamp\n};\n\nreturn transformedData;"
      },
      "typeVersion": 2
    },
    {
      "id": "node_8",
      "name": "Update Guest Success",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1860,
        -100
      ],
      "parameters": {
        "id": "={{$json.Id}}",
        "table": "NOCODB_TABLEID",
        "operation": "update",
        "projectId": "NOCODB_PROJECTID",
        "dataToSend": "autoMapInputData",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_9",
      "name": "Clear Broken LinkedIn URL",
      "type": "n8n-nodes-base.code",
      "position": [
        1660,
        260
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get the guest ID from the original NocoDB data\nlet guestId = null;\nlet linkedinUrl = null;\n\ntry {\n  guestId = $('Get Guests with LinkedIn').item.json.Id;\n  linkedinUrl = $('Get Guests with LinkedIn').item.json.LinkedIn;\n} catch (error) {\n  throw new Error('Could not find guest data. Error: ' + error.message);\n}\n\nif (!guestId) {\n  throw new Error('Guest ID is null or undefined');\n}\n\n// Get the current timestamp\nconst currentTimestamp = new Date().toISOString();\n\n// Return data to clear the broken LinkedIn URL\nreturn {\n  Id: guestId,\n  LinkedIn: null, // Clear the LinkedIn field\n  linkedin_scrape_status: 'invalid_url',\n  linkedin_scrape_error_reason: 'Profile not found or inaccessible (404)',\n  linkedin_last_modified: currentTimestamp\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "node_10",
      "name": "Update Guest - Clear URL",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1880,
        260
      ],
      "parameters": {
        "id": "={{$json.Id}}",
        "table": "NOCODB_TABLEID",
        "operation": "update",
        "projectId": "NOCODB_PROJECTID",
        "dataToSend": "autoMapInputData",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_11",
      "name": "Handle Scraper Error",
      "type": "n8n-nodes-base.code",
      "position": [
        1640,
        620
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get the guest ID and error info\nlet guestId = null;\nlet errorMessage = 'Unknown error';\n\ntry {\n  guestId = $('Get Guests with LinkedIn').item.json.Id;\n  \n  // Try to get error details from the failed request\n  if ($json.error) {\n    errorMessage = $json.error.message || $json.error;\n  } else if ($json.data && $json.data.status === 'FAILED') {\n    errorMessage = 'Apify scraper failed';\n  }\n} catch (error) {\n  errorMessage = error.message;\n}\n\n// Get the current timestamp\nconst currentTimestamp = new Date().toISOString();\n\n// Return error update data\nreturn {\n  Id: guestId,\n  linkedin_scrape_status: 'error',\n  linkedin_scrape_error_reason: errorMessage,\n  linkedin_scrape_last_attempt: currentTimestamp\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "node_12",
      "name": "Update Guest - Error Status",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1860,
        620
      ],
      "parameters": {
        "id": "={{$json.Id}}",
        "table": "NOCODB_TABLEID",
        "operation": "update",
        "projectId": "NOCODB_PROJECTID",
        "dataToSend": "autoMapInputData",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6356f2e6-a8d3-4d0f-8ac9-22793999d1bf",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        160,
        280
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ccaaf658-e84e-47cb-8519-f9547c9bf6eb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1540,
        -220
      ],
      "parameters": {
        "color": 4,
        "width": 580,
        "height": 320,
        "content": "## Data enrichment flow"
      },
      "typeVersion": 1
    },
    {
      "id": "142b5d5f-82fe-4f6d-b891-dd2eb1de646f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1540,
        140
      ],
      "parameters": {
        "width": 580,
        "height": 320,
        "content": "## Remove 404 linkedin URL"
      },
      "typeVersion": 1
    },
    {
      "id": "63b77aec-30df-4ee3-843e-57170c36b840",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1540,
        500
      ],
      "parameters": {
        "color": 3,
        "width": 580,
        "height": 320,
        "content": "## Apify error/timeout"
      },
      "typeVersion": 1
    },
    {
      "id": "ed6d5639-4a83-44de-9b38-a83a9ba3d342",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        320,
        120
      ],
      "parameters": {
        "color": 5,
        "width": 220,
        "height": 240,
        "content": "## Get LinkedIn URL"
      },
      "typeVersion": 1
    },
    {
      "id": "9d3e30ef-b19a-4a89-b968-a3dcba8e9efa",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        620,
        120
      ],
      "parameters": {
        "color": 5,
        "width": 420,
        "height": 240,
        "content": "## Get LinkedIn profile data"
      },
      "typeVersion": 1
    },
    {
      "id": "5de302ee-f413-4b95-b14e-677d9d3e793d",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        280
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "74ff9c6b-b162-4c0c-a33b-2129b4babe8d",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        280
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "1ce6b31d-bdc3-40e1-ab07-4368b410da6e",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2180,
        20
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 600,
        "content": "## Required fields in NocoDB table:\n\n**Input field:**\n* LinkedIn\n\n**Output fields:**\n* linkedin_url\n* linkedin_full_name\n* linkedin_first_name: \n* linkedin_headline:\n* linkedin_email:\n* linkedin_bio:\n* linkedin_profile_pic\n* linkedin_current_role\n* linkedin_current_company\n* linkedin_country\n* linkedin_skills\n* linkedin_company_website\n* linkedin_experiences\n* linkedin_personal_website\n* linkedin_publications\n* linkedin_scrape_error_reason\n* linkedin_scrape_last_attempt\n* linkedin_scrape_status\n* linkedin_last_modified"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true
  },
  "versionId": "3357614b-e310-44b8-97af-eac2106b28f4",
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Get Guests with LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform Data": {
      "main": [
        [
          {
            "node": "Update Guest Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Run Status": {
      "main": [
        [
          {
            "node": "Get Scraper Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Scraper Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Guests with LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Scraper Results": {
      "main": [
        [
          {
            "node": "Transform Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Clear Broken LinkedIn URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Completion": {
      "main": [
        [
          {
            "node": "Check Run Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Scraper Error": {
      "main": [
        [
          {
            "node": "Update Guest - Error Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Guests with LinkedIn": {
      "main": [
        [
          {
            "node": "Run Apify LinkedIn Scraper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clear Broken LinkedIn URL": {
      "main": [
        [
          {
            "node": "Update Guest - Clear URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Apify LinkedIn Scraper": {
      "main": [
        [
          {
            "node": "Wait for Completion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}