{
  "id": "z57chhZkWbOpBs5n",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Enrich leads with Firecrawl and store in Supabase",
  "tags": [],
  "nodes": [
    {
      "id": "82e8b97a-c916-41b0-b273-7fc8c31ab167",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        -320
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 112,
        "content": "## Supabase setup\nRun the SQL migration from the workflow README to create the `lead_enrichment` table with auto-updating timestamps."
      },
      "typeVersion": 1
    },
    {
      "id": "9ca4cd14-9fb6-4eb4-b48c-8d5b108da22f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        -512
      ],
      "parameters": {
        "width": 384,
        "height": 560,
        "content": "### How it works\n1. A webhook receives a company URL via POST request\n2. The URL is validated and normalized\n3. Firecrawl scrapes the website and searches for additional company data\n4. An AI agent (OpenRouter) extracts structured business signals: industry, pricing, tech stack, funding stage, hiring status, and more\n5. Supabase checks for duplicates, then saves the enriched profile\n6. The webhook returns the enriched result or a validation error\n\n### Setup\n1. Create a Supabase project and run the SQL from the \"Supabase setup\" sticky to create the `lead_enrichment` table\n2. Add your Firecrawl API key\n3. Add your OpenRouter API key (or swap for any OpenAI-compatible provider)\n4. Add your Supabase credentials (URL + service role key)\n5. Activate the workflow and send a POST request with `{\"url\": \"firecrawl.dev\"}` to the webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "62943a2f-b3c2-49f0-82c3-c53a0db40024",
      "name": "Receive company URL",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -464,
        96
      ],
      "parameters": {
        "path": "dedaa64a-3dc9-43ea-82ac-7fac034af0b2",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "4c7ca918-834c-4954-90ca-342757ed032e",
      "name": "Validate and normalize URL",
      "type": "n8n-nodes-base.code",
      "onError": "continueErrorOutput",
      "position": [
        -240,
        96
      ],
      "parameters": {
        "jsCode": "const body = $input.first().json.body;\nconst raw = body?.url?.trim();\n\nif (!raw) {\n  return [{\n    json: {\n      status: 422,\n      message: \"Missing 'url' field in request body.\"\n    }\n  }];\n}\n\n// Strip protocol and path to get clean domain\nconst domain = raw.replace(/^https?:\\/\\//i, \"\").replace(/\\/.*$/, \"\");\n\n// Validate domain format\nconst isValid = /^[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z]{2,})+$/.test(domain);\n\nif (!isValid) {\n  throw new Error(`Invalid URL: \"${raw}\" is not a valid domain or URL.`);\n}\nreturn [{\n  json: {\n    status: 200,\n    domain: domain,\n    url: `https://${domain}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9dd6f876-e97b-4d57-9c32-af4809e73d44",
      "name": "Extract business signals with AI",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        112,
        -160
      ],
      "parameters": {
        "text": "=url: {{ $json.url }}",
        "options": {
          "systemMessage": "You will receive a url, and should use firecrawl's tools to extract some of the business's signals. (Only use the search tool if you need more context). Then output structured data. Example:\n\n{\n  \"domain\": \"acme.io\",\n  \"company_name\": \"Acme\",\n  \"industry\": \"HR Tech\",\n  \"pricing_model\": \"subscription\",\n  \"has_free_trial\": true,\n  \"employee_signal\": \"50-200\",\n  \"funding_stage\": \"Series A\",\n  \"tech_stack\": [\"AWS\", \"Stripe\", \"Intercom\"],\n  \"integrations\": [\"Slack\", \"BambooHR\", \"Workday\"],\n  \"target_customer\": \"mid-market HR teams\",\n  \"trust_signals\": [\"SOC 2\", \"500+ customers\", \"4.8 on G2\"],\n  \"hiring\": true,\n  \"open_roles_count\": 12\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3.1
    },
    {
      "id": "48741495-021d-408a-8b79-7cb490d7f131",
      "name": "OpenRouter LLM",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -16,
        64
      ],
      "parameters": {
        "model": "anthropic/claude-sonnet-4.6",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fa0674b2-3f3f-45d7-b0e1-02db4e267d8f",
      "name": "Scrape company website",
      "type": "@mendable/n8n-nodes-firecrawl.firecrawlTool",
      "position": [
        144,
        64
      ],
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "parsers": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Parsers', ``, 'string') }}",
        "operation": "scrape",
        "requestOptions": {}
      },
      "credentials": {
        "firecrawlApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9701d370-aadc-4c39-b546-4224a49431f7",
      "name": "Search company data",
      "type": "@mendable/n8n-nodes-firecrawl.firecrawlTool",
      "position": [
        320,
        64
      ],
      "parameters": {
        "limit": 3,
        "query": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Query', ``, 'string') }}",
        "sources": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Sources', ``, 'string') }}",
        "resource": "MapSearch",
        "operation": "search",
        "requestOptions": {}
      },
      "credentials": {
        "firecrawlApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "427e93e2-595a-481f-83fe-749809eeb604",
      "name": "Parse into structured profile",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        480,
        64
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"domain\": \"acme.io\",\n  \"company_name\": \"Acme\",\n  \"industry\": \"HR Tech\",\n  \"pricing_model\": \"subscription\",\n  \"has_free_trial\": true,\n  \"employee_signal\": \"50-200\",\n  \"funding_stage\": \"Series A\",\n  \"tech_stack\": [\"AWS\", \"Stripe\", \"Intercom\"],\n  \"integrations\": [\"Slack\", \"BambooHR\", \"Workday\"],\n  \"target_customer\": \"mid-market HR teams\",\n  \"trust_signals\": [\"SOC 2\", \"500+ customers\", \"4.8 on G2\"],\n  \"hiring\": true,\n  \"open_roles_count\": 12\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "daccc2ad-5283-4c13-947a-640d2013718e",
      "name": "Check for duplicate in Supabase",
      "type": "n8n-nodes-base.supabase",
      "position": [
        576,
        -64
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "domain",
              "keyValue": "={{ $json.output.domain }}"
            }
          ]
        },
        "tableId": "lead_enrichment",
        "operation": "get"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "9d7822f4-441d-4d0a-b92f-f114567a00e2",
      "name": "Skip if already enriched",
      "type": "n8n-nodes-base.if",
      "position": [
        800,
        -64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "fc4c27fb-9647-4457-a19b-2737a50dfb9f",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{$input.all()[0].json}}",
              "rightValue": "1"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "9cf3219c-b671-40b4-be38-696e97c60d98",
      "name": "Prepare profile for insert",
      "type": "n8n-nodes-base.code",
      "position": [
        1056,
        48
      ],
      "parameters": {
        "jsCode": "return $('Extract business signals with AI').all().map(item => ({\n  json: item.json.output\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "5a1823a8-0293-4571-b577-6c32257afa18",
      "name": "Save enriched profile to Supabase",
      "type": "n8n-nodes-base.supabase",
      "position": [
        1232,
        48
      ],
      "parameters": {
        "tableId": "lead_enrichment",
        "dataToSend": "autoMapInputData"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b72fa969-c647-41b4-9789-7a39aa5affc3",
      "name": "Return enriched profile",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1424,
        -80
      ],
      "parameters": {
        "options": {
          "responseKey": "={{ $('Extract business signals with AI').item.json }}",
          "responseCode": 200
        }
      },
      "typeVersion": 1.5
    },
    {
      "id": "daca8e6b-8ad3-4850-a76e-7a39bd515e2e",
      "name": "Return URL validation error",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        176,
        240
      ],
      "parameters": {
        "options": {
          "responseKey": "={{ $json.error }}",
          "responseCode": 422
        }
      },
      "typeVersion": 1.5
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "c42d155b-80b4-476f-a30e-36548bcef29a",
  "connections": {
    "OpenRouter LLM": {
      "ai_languageModel": [
        [
          {
            "node": "Extract business signals with AI",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Receive company URL": {
      "main": [
        [
          {
            "node": "Validate and normalize URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search company data": {
      "ai_tool": [
        [
          {
            "node": "Extract business signals with AI",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Scrape company website": {
      "ai_tool": [
        [
          {
            "node": "Extract business signals with AI",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Skip if already enriched": {
      "main": [
        [
          {
            "node": "Return enriched profile",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare profile for insert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare profile for insert": {
      "main": [
        [
          {
            "node": "Save enriched profile to Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate and normalize URL": {
      "main": [
        [
          {
            "node": "Extract business signals with AI",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Return URL validation error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse into structured profile": {
      "ai_outputParser": [
        [
          {
            "node": "Extract business signals with AI",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Check for duplicate in Supabase": {
      "main": [
        [
          {
            "node": "Skip if already enriched",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract business signals with AI": {
      "main": [
        [
          {
            "node": "Check for duplicate in Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save enriched profile to Supabase": {
      "main": [
        [
          {
            "node": "Return enriched profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}