{
  "name": "Firecrawl Web Crawler Agent (Webhook)",
  "settings": {
    "executionOrder": "v1"
  },
  "nodes": [
    {
      "parameters": {
        "content": "## Firecrawl Web Crawler Agent \u2014 Webhook variant\n**What it does:** Programmatic POST endpoint exposing the same tool-using Firecrawl agent as `01-main-firecrawl-agent`, but for machine-to-machine use. Returns structured JSON.\n\n**Endpoint:** `POST /webhook/firecrawl-research`\n\n**Request body:**\n```\n{\n  \"prompt\": \"Research what Firecrawl offers and compare with Apify\",\n  \"output_schema\": {                  // optional JSON Schema \u2014 agent conforms output to this\n    \"type\": \"object\",\n    \"required\": [\"summary\", \"sources\"],\n    \"properties\": {\n      \"summary\": { \"type\": \"string\" },\n      \"sources\": { \"type\": \"array\" }\n    }\n  },\n  \"session_id\": \"optional-stable-id\", // default: auto-generated per request\n  \"credit_cap\": 200                    // default 200; soft cap enforced by the agent\n}\n```\n\n**Response:**\n```\n{\n  \"session_id\": \"...\",\n  \"result\": { /* output_schema-conforming, or default 5-section report */ },\n  \"credits_used_this_request\": 17\n}\n```\n\n### Required credentials\nSame as `01-main-firecrawl-agent`: Firecrawl API (HTTP Header Auth), OpenRouter API, Postgres (writable, `firecrawl_agent_rw` role).\n\n### Sub-workflows required (import first, then re-link)\nIdentical to the chat variant \u2014 this workflow shares the same 9 sub-workflows.\n\n### Auth\nThe default n8n webhook is unauthenticated. Enable `Header Auth` on the webhook node (a shared secret) or put n8n behind an API gateway / Cloudflare Worker for production use.",
        "height": 760,
        "width": 800,
        "color": 6
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -700,
        -820
      ],
      "id": "sticky-fc-webhook-header",
      "name": "README"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "firecrawl-research",
        "responseMode": "lastNode",
        "options": {
          "allowedOrigins": "*",
          "rawBody": false
        }
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -680,
        0
      ],
      "id": "fc-wh-trigger",
      "name": "POST /firecrawl-research"
    },
    {
      "parameters": {
        "jsCode": "const body = $input.first().json.body || $input.first().json;\nconst prompt = (body.prompt || '').toString().trim();\nif (!prompt) throw new Error('Request body must include a `prompt` string.');\n\nconst output_schema = body.output_schema || null;\nconst session_id = (body.session_id || $execution.id || 'no-session').toString();\nlet credit_cap = Number(body.credit_cap || 200);\nif (!Number.isFinite(credit_cap) || credit_cap < 0) credit_cap = 200;\n\nreturn [{ json: {\n  prompt,\n  output_schema,\n  session_id,\n  credit_cap,\n  has_schema: output_schema !== null\n} }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -460,
        0
      ],
      "id": "fc-wh-prep",
      "name": "Parse Request"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.prompt }}{{ $json.output_schema ? ('\\n\\nReturn ONLY valid JSON conforming to this schema:\\n' + JSON.stringify($json.output_schema)) : '' }}",
        "options": {
          "maxIterations": 12,
          "systemMessage": "You are a senior web research analyst powered by Firecrawl, invoked via a programmatic webhook.\n\nThe caller's request is in the user message. If their request includes a JSON schema at the end, your response MUST be a single JSON object conforming to that schema \u2014 no markdown fences, no explanatory prose, just the JSON.\n\nIf no schema was provided, return a JSON object with exactly these fields:\n```\n{\n  \"headline\": \"string \u2014 one-sentence key finding with the most important numbers\",\n  \"summary\":  \"string \u2014 2-4 short paragraphs with inline [n] citations\",\n  \"evidence\": [{\"finding\": \"...\", \"source\": \"[n]\", \"confidence\": \"high|medium|low\"}],\n  \"sources\":  [{\"n\": 1, \"title\": \"...\", \"url\": \"...\", \"scraped_at\": \"ISO-8601\"}],\n  \"observations\": [\"string\", ...],\n  \"credits_used_this_request\": <number from your last check_credits call>\n}\n```\n\n## Your tools (identical to the chat variant)\n\n**Discovery (cheap):**\n- `search_web(query, limit?, scrape_results?)` \u2014 2 credits per 10 results\n- `map_site(url, search?, limit?)` \u2014 1 credit flat (use this BEFORE crawl or batch_scrape)\n- `check_credits()` \u2014 0 credits. Call every 5 tool calls and BEFORE any crawl/batch/extract.\n\n**Scrape (credits apply):**\n- `scrape_url(url, formats?, only_main_content?, force_refresh?)` \u2014 1 credit, 24h cached\n- `batch_scrape(urls[], formats?)` \u2014 up to 25 URLs, 1 credit each\n- `crawl_site(url, max_pages?, max_depth?, include_patterns?, exclude_patterns?)` \u2014 caps at 50 pages, depth 3\n- `extract_data(urls[], prompt?, schema?)` \u2014 5 credits per URL\n\n**Advanced:**\n- `firecrawl_autopilot(prompt, url?, max_wait?)` \u2014 Firecrawl's server-side agent\n- `browser_session(action, session_id?, code?, language?, url?)` \u2014 persistent browser\n- `think(thought)` \u2014 scratchpad\n\n## WINNING PATTERNS \u2014 follow in order\n1. **Map-first:** for any site-level question, `map_site` \u2192 filter via `think` \u2192 `batch_scrape`. Never crawl blind.\n2. **Cache-aware:** trust `cache_hit: true` \u2014 don't re-scrape.\n3. **Credit discipline:** call `check_credits` every ~5 tools and BEFORE any crawl/batch/extract. If `over_budget` or `near_cap`, STOP and summarize with what you have.\n4. **Citation-first:** every factual claim needs an inline `[n]` mapped to the Sources array.\n5. **Tool decision table:**\n   - Single URL \u2192 scrape_url\n   - Find pages \u2192 search_web\n   - Site section \u2192 map_site + batch_scrape\n   - Structured fields \u2192 extract_data with schema\n   - JS-heavy / login-gated \u2192 browser_session or firecrawl_autopilot\n\n## Hard rules\n- No writes, no destructive ops. You read the web + read/write ledger and cache.\n- Never invent URLs. Never invent facts. Everything traceable.\n- If a tool errors: `think` \u2192 diagnose \u2192 retry ONCE. After 2 failures, include the error in observations and return what you have.\n- maxIterations is 12. Budget accordingly.\n- Your entire response must be a single JSON object. No markdown fences. No explanatory text outside the JSON.",
          "returnIntermediateSteps": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        -240,
        0
      ],
      "id": "fc-wh-agent",
      "name": "Firecrawl Web Agent (Webhook)"
    },
    {
      "parameters": {
        "model": "anthropic/claude-sonnet-4.6",
        "options": {
          "temperature": 0.2
        }
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        -400,
        260
      ],
      "id": "fc-wh-llm",
      "name": "OpenRouter Chat Model"
    },
    {
      "parameters": {
        "name": "search_web",
        "description": "Searches the open web via Firecrawl. Returns up to `limit` ranked results. Costs 2 credits per 10 results. Inputs: query, limit (default 5, max 20), scrape_results (default false).",
        "workflowId": {
          "__rl": true,
          "value": "Fc04SubSearch01x",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "query": "={{ $fromAI('query', 'Search query.', 'string') }}",
            "limit": "={{ $fromAI('limit', 'Max results (1-20).', 'number') }}",
            "scrape_results": "={{ $fromAI('scrape_results', 'Scrape each result (+1 credit each).', 'boolean') }}",
            "session_id": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "query",
              "displayName": "query",
              "required": true,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "limit",
              "displayName": "limit",
              "required": false,
              "type": "number",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "scrape_results",
              "displayName": "scrape_results",
              "required": false,
              "type": "boolean",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        -100,
        260
      ],
      "id": "fc-wh-tool-search",
      "name": "search_web"
    },
    {
      "parameters": {
        "name": "map_site",
        "description": "Returns every URL on a site. 1 credit flat. Use BEFORE crawl_site or batch_scrape. Inputs: url, search (optional filter), limit (default 500).",
        "workflowId": {
          "__rl": true,
          "value": "Fc05SubMapSite01",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "url": "={{ $fromAI('url', 'Site root URL.', 'string') }}",
            "search": "={{ $fromAI('search', 'Optional path keyword filter.', 'string') }}",
            "limit": "={{ $fromAI('limit', 'Max URLs returned.', 'number') }}",
            "session_id": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "url",
              "displayName": "url",
              "required": true,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "search",
              "displayName": "search",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "limit",
              "displayName": "limit",
              "required": false,
              "type": "number",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        80,
        260
      ],
      "id": "fc-wh-tool-map",
      "name": "map_site"
    },
    {
      "parameters": {
        "name": "scrape_url",
        "description": "Scrapes one URL. 1 credit. 24h Postgres cache (cache_hit=true means 0 credits). Inputs: url, formats (default ['markdown','links']), only_main_content (default true), force_refresh (default false).",
        "workflowId": {
          "__rl": true,
          "value": "Fc03SubScrapeC01",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "url": "={{ $fromAI('url', 'URL to scrape.', 'string') }}",
            "formats": "={{ $fromAI('formats', 'Output formats array.', 'json') }}",
            "only_main_content": "={{ $fromAI('only_main_content', 'Strip nav/footer/ads.', 'boolean') }}",
            "force_refresh": "={{ $fromAI('force_refresh', 'Bypass 24h cache.', 'boolean') }}",
            "session_id": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "url",
              "displayName": "url",
              "required": true,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "formats",
              "displayName": "formats",
              "required": false,
              "type": "array",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "only_main_content",
              "displayName": "only_main_content",
              "required": false,
              "type": "boolean",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "force_refresh",
              "displayName": "force_refresh",
              "required": false,
              "type": "boolean",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        260,
        260
      ],
      "id": "fc-wh-tool-scrape",
      "name": "scrape_url"
    },
    {
      "parameters": {
        "name": "batch_scrape",
        "description": "Scrapes up to 25 URLs in parallel. 1 credit per successful URL. Inputs: urls (array, max 25), formats (default ['markdown']), only_main_content (default true).",
        "workflowId": {
          "__rl": true,
          "value": "Fc07SubBatchSc01",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "urls": "={{ $fromAI('urls', 'Array of URLs, max 25.', 'json') }}",
            "formats": "={{ $fromAI('formats', 'Formats array.', 'json') }}",
            "only_main_content": "={{ $fromAI('only_main_content', 'Strip nav/footer/ads.', 'boolean') }}",
            "session_id": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "urls",
              "displayName": "urls",
              "required": true,
              "type": "array",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "formats",
              "displayName": "formats",
              "required": false,
              "type": "array",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "only_main_content",
              "displayName": "only_main_content",
              "required": false,
              "type": "boolean",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        440,
        260
      ],
      "id": "fc-wh-tool-batch",
      "name": "batch_scrape"
    },
    {
      "parameters": {
        "name": "crawl_site",
        "description": "Bounded recursive crawl. Max 50 pages, depth 3. 1 credit per page. Prefer map_site + batch_scrape. Inputs: url, max_pages (default 25), max_depth (default 2), include_patterns, exclude_patterns.",
        "workflowId": {
          "__rl": true,
          "value": "Fc06SubCrawlB01x",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "url": "={{ $fromAI('url', 'Site/starting URL.', 'string') }}",
            "max_pages": "={{ $fromAI('max_pages', 'Max pages to scrape.', 'number') }}",
            "max_depth": "={{ $fromAI('max_depth', 'Max link-depth.', 'number') }}",
            "include_patterns": "={{ $fromAI('include_patterns', 'URL glob includes.', 'json') }}",
            "exclude_patterns": "={{ $fromAI('exclude_patterns', 'URL glob excludes.', 'json') }}",
            "session_id": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "url",
              "displayName": "url",
              "required": true,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "max_pages",
              "displayName": "max_pages",
              "required": false,
              "type": "number",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "max_depth",
              "displayName": "max_depth",
              "required": false,
              "type": "number",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "include_patterns",
              "displayName": "include_patterns",
              "required": false,
              "type": "array",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "exclude_patterns",
              "displayName": "exclude_patterns",
              "required": false,
              "type": "array",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        620,
        260
      ],
      "id": "fc-wh-tool-crawl",
      "name": "crawl_site"
    },
    {
      "parameters": {
        "name": "extract_data",
        "description": "LLM structured extraction. 5 credits per URL, max 10 URLs. Inputs: urls, prompt (natural-language) OR schema (JSON Schema). Returns JSON + schema_valid flag.",
        "workflowId": {
          "__rl": true,
          "value": "Fc08SubExtract01",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "urls": "={{ $fromAI('urls', 'URLs to extract from.', 'json') }}",
            "prompt": "={{ $fromAI('prompt', 'What to extract (NL description).', 'string') }}",
            "schema": "={{ $fromAI('schema', 'Optional JSON Schema for output.', 'json') }}",
            "session_id": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "urls",
              "displayName": "urls",
              "required": true,
              "type": "array",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "prompt",
              "displayName": "prompt",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "schema",
              "displayName": "schema",
              "required": false,
              "type": "object",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        800,
        260
      ],
      "id": "fc-wh-tool-extract",
      "name": "extract_data"
    },
    {
      "parameters": {
        "name": "firecrawl_autopilot",
        "description": "Delegates to Firecrawl's server-side agent. Variable credits (5-30 typical). Inputs: prompt (required), url (optional), max_wait (default 180, max 300).",
        "workflowId": {
          "__rl": true,
          "value": "Fc09SubFcAgent01",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "prompt": "={{ $fromAI('prompt', 'Research goal.', 'string') }}",
            "url": "={{ $fromAI('url', 'Starting URL hint.', 'string') }}",
            "max_wait": "={{ $fromAI('max_wait', 'Seconds to wait (max 300).', 'number') }}",
            "session_id": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "prompt",
              "displayName": "prompt",
              "required": true,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "url",
              "displayName": "url",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "max_wait",
              "displayName": "max_wait",
              "required": false,
              "type": "number",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        980,
        260
      ],
      "id": "fc-wh-tool-autopilot",
      "name": "firecrawl_autopilot"
    },
    {
      "parameters": {
        "name": "browser_session",
        "description": "Persistent browser sandbox. Actions: create, execute, delete, list. ALWAYS delete when done.",
        "workflowId": {
          "__rl": true,
          "value": "Fc10SubSandbox01",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "action": "={{ $fromAI('action', 'create|execute|delete|list.', 'string') }}",
            "session_id": "={{ $fromAI('session_id', 'Sandbox session id.', 'string') }}",
            "code": "={{ $fromAI('code', 'Code to execute.', 'string') }}",
            "language": "={{ $fromAI('language', 'javascript|python|bash.', 'string') }}",
            "url": "={{ $fromAI('url', 'Starting URL for create.', 'string') }}",
            "session_id_caller": "={{ $('Parse Request').first().json.session_id }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "action",
              "displayName": "action",
              "required": true,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "code",
              "displayName": "code",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "language",
              "displayName": "language",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "url",
              "displayName": "url",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "session_id_caller",
              "displayName": "session_id_caller",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        1160,
        260
      ],
      "id": "fc-wh-tool-sandbox",
      "name": "browser_session"
    },
    {
      "parameters": {
        "name": "check_credits",
        "description": "Aggregate credit spend for this session. 0 credits. Call every 5 tools and BEFORE any crawl/batch/extract.",
        "workflowId": {
          "__rl": true,
          "value": "Fc11SubCredits01",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "session_id": "={{ $('Parse Request').first().json.session_id }}",
            "credit_cap": "={{ $('Parse Request').first().json.credit_cap }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "type": "string",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            },
            {
              "id": "credit_cap",
              "displayName": "credit_cap",
              "required": false,
              "type": "number",
              "display": true,
              "canBeUsedToMatch": true,
              "defaultMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.1,
      "position": [
        1340,
        260
      ],
      "id": "fc-wh-tool-credits",
      "name": "check_credits"
    },
    {
      "parameters": {
        "name": "think",
        "description": "Internal scratchpad. Input: thought (string). Returns the thought with a timestamp. Use generously.",
        "language": "javaScript",
        "jsCode": "const t = typeof query === 'string' ? query : (query && query.thought) ? query.thought : JSON.stringify(query);\nreturn `Thought recorded at ${new Date().toISOString()}: ${t}`;",
        "specifyInputSchema": true,
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"thought\": { \"type\": \"string\", \"description\": \"What you're planning, filtering, or diagnosing.\" }\n  },\n  \"required\": [\"thought\"]\n}"
      },
      "type": "@n8n/n8n-nodes-langchain.toolCode",
      "typeVersion": 1.3,
      "position": [
        1520,
        260
      ],
      "id": "fc-wh-tool-think",
      "name": "think"
    },
    {
      "parameters": {
        "jsCode": "const agentOut = $input.first().json;\nconst req = $('Parse Request').first().json;\n\n// agentOut.output is the agent's final answer (stringified JSON in most cases)\nlet result = agentOut.output || agentOut;\nif (typeof result === 'string') {\n  // Try to parse JSON from the agent output \u2014 strip fences if present\n  let s = result.trim().replace(/^```(?:json)?\\s*/i, '').replace(/```\\s*$/, '');\n  try { result = JSON.parse(s); } catch { /* keep as string */ }\n}\n\nreturn [{ json: {\n  session_id: req.session_id,\n  prompt: req.prompt,\n  output_schema_provided: req.has_schema,\n  result\n} }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -20,
        0
      ],
      "id": "fc-wh-shape",
      "name": "Shape Response"
    }
  ],
  "connections": {
    "POST /firecrawl-research": {
      "main": [
        [
          {
            "node": "Parse Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Request": {
      "main": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "search_web": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "map_site": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "scrape_url": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "batch_scrape": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "crawl_site": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "extract_data": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "firecrawl_autopilot": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "browser_session": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "check_credits": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "think": {
      "ai_tool": [
        [
          {
            "node": "Firecrawl Web Agent (Webhook)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Firecrawl Web Agent (Webhook)": {
      "main": [
        [
          {
            "node": "Shape Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}