AutomationFlowsAI & RAG › Enrich Company Leads with Firecrawl, OpenRouter AI, and Supabase

Enrich Company Leads with Firecrawl, OpenRouter AI, and Supabase

ByFirecrawl @firecrawl on n8n.io

What this does

Webhook trigger★★★★☆ complexityAI-powered15 nodesAgentOpenRouter Chat@Mendable/N8N Nodes FirecrawlOutput Parser StructuredSupabase
AI & RAG Trigger: Webhook Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #13910 — we link there as the canonical source.

This workflow follows the Agent → OpenRouter Chat recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "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
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

What this does

Source: https://n8n.io/workflows/13910/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Turn any prompt into structured web data. Send a POST request with a natural language prompt and an optional JSON schema, and get back clean, structured results scraped from the web by an AI agent pow

Agent, OpenRouter Chat, Output Parser Structured +1
AI & RAG

⏺ 🚀 How it works

Agent, Anthropic Chat, Output Parser Structured +6
AI & RAG

Production-ready Reddit lead generation system with progressive data loading for optimal UX. This workflow integrates with a web frontend, sending results in real-time as they're processed instead of

Output Parser Structured, Agent, Reddit +3
AI & RAG

This workflow automatically processes new free-trial / lead sign-ups in real time: Catches a webhook from any source (Webflow form, Intercom, custom agent, etc.) Filters out personal / disposable / .e

Output Parser Structured, Agent, HTTP Request +7
AI & RAG

Transform your WhatsApp group conversations into actionable business intelligence through automated AI analysis and daily reporting. This workflow eliminates manual conversation monitoring by capturin

OpenRouter Chat, Output Parser Autofixing, Agent +6