AutomationFlowsAI & RAG › Research Us Legal Regulations with Courtlistener, Legiscan, Openrouter and…

Research Us Legal Regulations with Courtlistener, Legiscan, Openrouter and…

Original n8n title: Research Us Legal Regulations with Courtlistener, Legiscan, Openrouter and Web Search

ByOpen Paws @openpaws on n8n.io

This workflow is designed for legal professionals, policy analysts, and compliance teams who need to: Research case law, legislation, and regulatory developments on specific topics Build comprehensive legal intelligence reports from authoritative sources Track enforcement trends…

Webhook trigger★★★★★ complexityAI-powered45 nodesHTTP Request ToolTool Http RequestTool ThinkJina Ai ToolChain LlmOutput Parser StructuredAgentOpenRouter Chat
AI & RAG Trigger: Webhook Nodes: 45 Complexity: ★★★★★ AI nodes: yes Added:
Research Us Legal Regulations with Courtlistener, Legiscan, Openrouter and… — n8n workflow card showing HTTP Request Tool, Tool Http Request, Tool Think integration

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

This workflow follows the Agent → Chainllm 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": "PbTGOOYr8wqoBYsBnDYAR",
  "name": "Research US regulations using AI agents with web search and report generation",
  "tags": [],
  "nodes": [
    {
      "id": "05ae5f0c-e4bc-4d62-a587-a87338a28f8e",
      "name": "Court Listener Discovery",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        816,
        496
      ],
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "toolDescription": "=**Discovery Phase - Find ALL Court Cases**\n**Base URL:** https://www.courtlistener.com/api/rest/v4/\n**ALWAYS include full base URL in requests**\n\n## Search Endpoint (CORRECTED)\nhttps://www.courtlistener.com/api/rest/v4/search/?type=o&q={query}&page_size=20\n\n## Critical Parameters\n- **type=o** (opinions - most common)\n- **type=r** (RECAP docket documents)\n- **q={search terms}** (URL encode special characters)\n- **page_size=20** (API maximum)\n- **order_by=-score** (best matches first)\n- **court__jurisdiction=F** (F=federal, S=state, optional)\n\n## WORKING Discovery Search URLs\nCompany name searches:\nhttps://www.courtlistener.com/api/rest/v4/search/?type=o&q=cargill&page_size=20\nhttps://www.courtlistener.com/api/rest/v4/search/?type=o&q=\"cargill+meat\"&page_size=20\nhttps://www.courtlistener.com/api/rest/v4/search/?type=o&q=cargill+violation&page_size=20\n\nIssue/violation searches:\nhttps://www.courtlistener.com/api/rest/v4/search/?type=o&q=animal+welfare+cargill&page_size=20\nhttps://www.courtlistener.com/api/rest/v4/search/?type=o&q=environmental+violation+cargill&page_size=20\n\nDocket searches (different endpoint):\nhttps://www.courtlistener.com/api/rest/v4/dockets/?case_name__icontains=cargill&page_size=20\nhttps://www.courtlistener.com/api/rest/v4/dockets/?case_name__icontains=cargill&date_filed__gte=2020-01-01\n\n## Response Structure\n```json\n{\n  \"count\": 123,\n  \"next\": \"https://www.courtlistener.com/api/rest/v4/search/?page=2...\",\n  \"results\": [{\n    \"id\": 456789,  // CAPTURE THIS for retrieval phase\n    \"cluster_id\": 123456,  // Alternative ID for cluster retrieval\n    \"case_name\": \"NPPC v. Ross\",\n    \"absolute_url\": \"/opinion/456789/nppc-v-ross/\",  // Note: partial path\n    \"court\": \"Court of Appeals for the Ninth Circuit\",\n    \"date_filed\": \"2022-03-15\",\n    \"snippet\": \"...text preview with <mark>highlighted</mark> terms...\"\n  }]\n}",
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "ecfc05f2-4305-4b4d-9f31-76ddea0a4340",
      "name": "Google Search Discovery",
      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
      "position": [
        368,
        496
      ],
      "parameters": {
        "url": "https://google.serper.dev/{endpoint}",
        "sendQuery": true,
        "sendHeaders": true,
        "specifyQuery": "model",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "toolDescription": "=**Discovery Phase - Find ALL Legal Analysis & News**\n\nSearches Google for comprehensive coverage of the query. Cast wide net.\n\n## Endpoint Selection\nYou must specify one of these endpoints:\n- search (general web search) - for legal analysis, expert commentary\n- news (news articles) - for recent coverage, breaking developments\n- scholar (academic papers) - for law review articles\n\n## Query Parameters\n{\n  \"q\": \"search query\",           // REQUIRED\n  \"location\": \"United States\",   // optional\n  \"tbs\": \"qdr:y\"                 // optional time filter\n}\n\n## Time Parameters (tbs)\n- \"qdr:d\" (past day)\n- \"qdr:w\" (past week) \n- \"qdr:m\" (past month)\n- \"qdr:y\" (past year)\n\n## Discovery Examples\nsearch endpoint: {\"q\": \"prop 12 legal analysis\", \"tbs\": \"qdr:y\"}\nnews endpoint: {\"q\": \"prop 12 constitutional challenge\", \"tbs\": \"qdr:m\"}\nsearch endpoint: {\"q\": \"proposition 12 california farm\"}\nscholar endpoint: {\"q\": \"prop 12 dormant commerce clause\"}\n\n**In Discovery Phase:**\n- Use multiple search variations\n- Try different endpoints\n- Don't filter results yet\n- Find everything relevant for later prioritization",
        "parametersHeaders": {
          "values": [
            {
              "name": "Content-Type",
              "value": "application/json",
              "valueProvider": "fieldValue"
            }
          ]
        },
        "placeholderDefinitions": {
          "values": [
            {
              "name": "{endpoint}",
              "description": "search - General web search \n\nnews - News articles \n\nscholar - Academic papers \n\npatents - Patent searches \n\nmaps - Location-based searches \n\nreviews - Business/product reviews"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1
    },
    {
      "id": "09bdaf84-2695-4f0d-a9ed-0ace7e37527d",
      "name": "LegiScan Discovery",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        512,
        496
      ],
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "toolDescription": "=**Discovery Phase - Find ALL Related Legislation**\n\n**CRITICAL: ALWAYS use full base URL**\n**Base URL:** https://api.legiscan.com/\n**Every request MUST start with:** https://api.legiscan.com/?op=\n\n## Search Operation\nhttps://api.legiscan.com/?op=getSearch&state={state}&query={query}&year={year}\n\n## Parameters\n- state: ALL (searches all states) or specific state code (CA, TX, etc.)\n- query: search terms (URL encoded)\n- year: 1 (all years), 2 (current session), or specific year\n\n## CORRECT Discovery URLs\n\u2705 https://api.legiscan.com/?op=getSearch&state=ALL&query=prop%2012&year=2\n\u2705 https://api.legiscan.com/?op=getSearch&state=ALL&query=proposition%2012\n\u2705 https://api.legiscan.com/?op=getSearch&state=CA&query=farm%20animal%20confinement\n\n## WRONG (Missing base URL)\n\u274c ?op=getSearch&state=ALL&query=prop%2012\n\u274c getSearch&state=ALL&query=prop%2012\n\n## Response Structure\n{\n  \"status\": \"OK\",\n  \"searchresult\": {\n    \"summary\": {\"count\": 15},\n    \"0\": {\n      \"bill_id\": 1234567,\n      \"bill_number\": \"AB 123\",\n      \"title\": \"Farm Animal Confinement Standards\"\n    }\n  }\n}\n\n**In Discovery Phase:**\n- ALWAYS use state=ALL for comprehensive search\n- Try multiple search terms\n- Note bill_id for later retrieval\n- Don't get full text yet"
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "10cd7049-4495-44d8-abaf-e23e5212c9a3",
      "name": "Court Listener Retrieveal",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        2224,
        496
      ],
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "toolDescription": "=**Retrieval Phase - Get Full Opinion Text**\n\nBase URL: https://www.courtlistener.com/api/rest/v4/\n\nYour job: Retrieve ONLY opinions Agent 2 selected. NO NEW SEARCHES.\n\nRetrieval URLs:\nhttps://www.courtlistener.com/api/rest/v4/opinions/{opinion_id}/\nhttps://www.courtlistener.com/api/rest/v4/clusters/{cluster_id}/\n\nAdd parameters to get full text:\n?fields=plain_text,html_with_citations,case_name\n\nExample:\nhttps://www.courtlistener.com/api/rest/v4/opinions/456789/?fields=plain_text,case_name\n\nResponse contains:\n- plain_text: Full opinion text\n- case_name: Title\n- date_filed: Date\n\nInclude complete opinion text in output for Agent 4.",
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "761fbb8b-0afc-44c5-9f8e-fa09f630934a",
      "name": "Think Tool Prioritization",
      "type": "@n8n/n8n-nodes-langchain.toolThink",
      "position": [
        1392,
        496
      ],
      "parameters": {
        "description": "=**Prioritization Phase - Score and Select Best Items**\n\nYour CRITICAL job: Analyze ALL discovery results and select 3-10 most relevant.\n\nRequired Process:\n1. Count everything found:\n   \"Found 23 DocumentCloud docs, 15 bills, 8 cases, 12 articles\"\n\n2. Score each item (1-10):\n   - 9-10: Direct query match + recent + authoritative\n   - 7-8: Strong relevance, important source\n   - 5-6: Moderate relevance\n   - 1-4: Tangential, skip\n\n3. Select top items with reasoning:\n   \"Doc 789123 (Score: 9/10) - 2024 enforcement report, direct match\n    Case 456 NPPC v. Ross (Score: 10/10) - Main constitutional challenge\n    Bill CA-1234 (Score: 8/10) - Original Prop 12 text\"\n\n4. Document what you're skipping:\n   \"Skipping: 18 older docs (pre-2020), duplicate news coverage, \n    tangential references, draft bills that didn't pass\"\n\nOutput Format:\n\"Analysis complete. Selected for retrieval:\nDocumentCloud: [List IDs, slugs, reasons]\nBills: [List bill IDs, reasons]  \nCases: [List case IDs, reasons]\nURLs: [List URLs, reasons]\"\n\nThis is your ONLY chance to prioritize - be strategic!"
      },
      "typeVersion": 1
    },
    {
      "id": "a38d1b00-948b-451d-b6ab-c8890508f8cf",
      "name": "LegiScan Retrieval",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        2096,
        496
      ],
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "toolDescription": "=**Retrieval Phase - Get Full Bill Text**\n\nBase URL: https://api.legiscan.com/\nCRITICAL: ALWAYS use full URL!\n\nYour job: Retrieve ONLY bills Agent 2 selected. NO NEW SEARCHES.\n\nBill Retrieval URL:\n\u2705 https://api.legiscan.com/?op=getBill&id={bill_id}\n\u274c ?op=getBill&id={bill_id} (WRONG - missing base URL!)\n\nExample:\nhttps://api.legiscan.com/?op=getBill&id=1234567\n\nResponse contains:\n{\n  \"bill\": {\n    \"bill_id\": 1234567,\n    \"title\": \"Full title\",\n    \"description\": \"Summary\",\n    \"sponsors\": [...],\n    \"texts\": [{\"doc_id\": 890, \"type\": \"Introduced\", \"url\": \"...\"}],\n    \"history\": [...],\n    \"votes\": [...]\n  }\n}\n\nTo get actual text:\nhttps://api.legiscan.com/?op=getBillText&id={doc_id}\n\nInclude full bill text and metadata for Agent 4."
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "865b9800-88c7-4114-a018-236b5c18298b",
      "name": "DocumentCloud Retrieval",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        2352,
        496
      ],
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          }
        },
        "toolDescription": "=**Retrieval Phase - Get Document Full Text**\n\nBase URL: https://s3.documentcloud.org/\n\nYour job: Retrieve text for ONLY documents Agent 2 selected.\n\nCRITICAL URL PATTERN:\nhttps://s3.documentcloud.org/documents/{id}/{slug}.txt.json\n\nALWAYS INCLUDE THE SLASHES!\n\n\u2705 CORRECT: https://s3.documentcloud.org/documents/789123/report.txt.json\n\u274c WRONG: https://s3.documentcloud.org/documents/789123-report.txt.json\n\nConstruction:\n1. Take ID from Agent 2: \"789123\"\n2. Take slug from Agent 2: \"usda-report\"\n3. Build full URL: https://s3.documentcloud.org/documents/789123/usda-report.txt.json\n\nResponse:\n{\n  \"pages\": [\n    {\"page\": 0, \"contents\": \"Full text of page 1...\"},\n    {\"page\": 1, \"contents\": \"Full text of page 2...\"}\n  ]\n}"
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "4da35866-71e3-4bc5-afa9-37a6854d8ba1",
      "name": "Jina URL Text Extraction",
      "type": "n8n-nodes-base.jinaAiTool",
      "position": [
        1952,
        496
      ],
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "options": {},
        "requestOptions": {},
        "descriptionType": "manual",
        "toolDescription": "=**Retrieval Phase - Extract Article Content**\n\nYour job: Get full text from ONLY URLs Agent 2 selected.\n\nSimply pass each prioritized URL:\nhttps://example.com/legal-analysis-article\n\nJina will:\n- Extract main content\n- Remove ads, navigation\n- Return clean text\n- Include title, author, date\n\nOutput: Complete article text for Agent 4's analysis.\n\nNo searching - only extract from Agent 2's selected URLs."
      },
      "credentials": {
        "jinaAiApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "82349c07-ce6f-4ae1-94de-da7309a507b7",
      "name": "Think Tool Analysis",
      "type": "@n8n/n8n-nodes-langchain.toolThink",
      "position": [
        3120,
        512
      ],
      "parameters": {
        "description": "=**Analysis Phase - Synthesize All Retrieved Texts**\n\nYour job: Analyze retrieved content to create strategic insights.\n\nRequired Analysis:\n1. Map findings to user query:\n   \"User asked about Prop 12 challenges\"\n   \n2. Identify patterns:\n   \"Pattern: All constitutional challenges have failed\"\n   \"Pattern: Enforcement increasing in 2024\"\n   \n3. Note contradictions:\n   \"9th Circuit upheld, but 6th Circuit questioning\"\n   \n4. Identify gaps:\n   \"No enforcement data after July 2024\"\n   \n5. Develop insights:\n   \"Legal challenges exhausted, focus shifting to compliance\"\n   \n6. Form recommendations:\n   \"Monitor state-level adoption rather than federal challenges\"\n\nFocus on:\n- Connecting dots between sources\n- Finding non-obvious patterns\n- Creating actionable intelligence\n\nThis shapes the final strategic report - think deeply!"
      },
      "typeVersion": 1
    },
    {
      "id": "1c55a7b6-46e2-4579-b6d0-ab7ef04d305b",
      "name": "Step 5: Verification",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        3680,
        240
      ],
      "parameters": {
        "text": "=# \ud83d\udd0d LEGAL INTELLIGENCE VERIFICATION AGENT\n\n## THE CURRENT DATE & TIME IS {{ $now }}\n\n## Mission\nIdentify **only clear factual fabrications about legal matters that would materially mislead on the law**. The report writer creates adaptive, strategically-focused legal intelligence - respect and protect this approach. Focus only on catching completely false legal claims, not questioning legal strategy or analysis.\n\n## Your Inputs\n1. **Final Legal Intelligence Report** - The report (likely adapted to specific legal query)\n2. **Retrieved Legal Documents** - Court opinions, statutes, regulations, etc.\n\n## CRITICAL PRINCIPLE: Protect Legal Analysis\n\n### The Report Writer Is Supposed To:\n- **Adapt structure to legal query** - Litigation strategy differs from compliance review\n- **Lead with most relevant legal findings** - Not comprehensive case law surveys\n- **Provide strategic legal recommendations** - This is the core value\n- **Connect precedents creatively** - Building legal arguments is the point\n- **Assess probability and risk** - Professional judgment is expected\n- **Make tactical suggestions** - Discovery strategies, negotiation points, etc.\n\n### Only Flag Clear Legal Fabrications:\n- **Completely wrong case names or citations**\n- **Entirely fictional legal rulings**\n- **Statutes that don't exist**\n- **Drastically misrepresented holdings**\n\n## WHAT IS DEFINITELY NOT HALLUCINATION\n\n### 1. ADAPTIVE LEGAL STRUCTURE\n**Always Acceptable:**\n- Starting with \"You have strong claims\" instead of \"Background Law\"\n- Organizing by litigation strategy rather than chronological case law\n- Custom sections like \"Discovery Opportunities\" or \"Settlement Leverage\"\n- Focusing entirely on compliance gaps if that's what was asked\n- Omitting legal history if not relevant to query\n\n### 2. LEGAL STRATEGY & RECOMMENDATIONS\n**Always Acceptable (This is the value!):**\n- \"File motion for summary judgment based on...\"\n- \"Strongest argument is promissory estoppel\"\n- \"Discovery should focus on emails regarding...\"\n- \"Settlement range likely $X-Y based on comparable cases\"\n- \"Judge Smith typically favors this argument\"\n- \"Regulatory risk is high given recent enforcement\"\n\n### 3. LEGAL ANALYSIS & INTERPRETATION\n**Always Acceptable:**\n- \"This precedent suggests courts would likely...\"\n- \"The statutory language implies...\"\n- \"Recent enforcement trends indicate...\"\n- \"This creates a circuit split with...\"\n- \"Distinguished from Smith because...\"\n- \"Analogous to Jones reasoning...\"\n\n### 4. RISK ASSESSMENTS & PREDICTIONS\n**Always Acceptable:**\n- \"70% likelihood of surviving motion to dismiss\"\n- \"High probability of regulatory scrutiny\"\n- \"Low risk given safe harbor provisions\"\n- \"Expect counterclaims for...\"\n- \"Agency likely to focus on...\"\n- \"Court would probably apply...\"\n\n### 5. PROCEDURAL RECOMMENDATIONS\n**Always Acceptable:**\n- \"File in Eastern District for favorable precedent\"\n- \"Request expedited discovery on...\"\n- \"Move to compel within 30 days\"\n- \"Seek preliminary injunction immediately\"\n- \"Consider removal to federal court\"\n- \"Demand jury trial for sympathy factor\"\n\n### 6. REASONABLE LEGAL INFERENCES\n**Always Acceptable:**\n- How courts typically interpret similar language\n- Standard litigation timelines\n- Usual settlement patterns\n- Common regulatory positions\n- Typical damages calculations\n- Expected procedural rulings\n\n## ACTUAL HALLUCINATIONS TO FLAG (VERY RARE)\n\n### 1. COMPLETELY WRONG CASE LAW\n**Only Flag If Entirely Fabricated:**\n- Case that never existed (Smith v. Jones when no such case)\n- Drastically wrong holding (opposite of actual ruling)\n- Wrong court level (Supreme Court when actually district court)\n\n**Don't Flag:**\n- Paraphrased holdings that capture essence\n- Combined reasoning from multiple cases\n- Slightly imprecise case names\n- Minor date variations\n\n### 2. FICTIONAL STATUTES/REGULATIONS\n**Only Flag If Doesn't Exist:**\n- Statute number that's completely wrong\n- Regulation that was never enacted\n- Law from wrong jurisdiction claimed\n\n**Don't Flag:**\n- Paraphrased statutory language\n- Summarized regulatory requirements\n- Combined provisions\n- General references to \"regulations\"\n\n### 3. MATERIALLY FALSE LEGAL FACTS\n**Only Flag If Would Change Legal Analysis:**\n- Statute of limitations off by years\n- Wrong burden of proof entirely\n- Incorrect jurisdiction fundamentally\n- False procedural requirements\n\n**Don't Flag:**\n- Minor procedural variations\n- Approximate timelines\n- General legal principles\n- Strategic assessments\n\n## VERIFICATION APPROACH FOR LEGAL INTELLIGENCE\n\n### First, Understand the Legal Context\n- What legal question was asked?\n- Is this litigation, compliance, or risk assessment?\n- Does the structure serve that purpose?\n- Is the legal strategy sound even if citations imperfect?\n\n### Apply Maximum Permissiveness\n- **Default to acceptable** for all strategic content\n- **Protect legal analysis** as core value\n- **Preserve tactical recommendations** completely\n- **Allow creative legal arguments**\n- **Respect professional judgment**\n\n### Only Flag If All True:\n1. It's a specific legal fact (not strategy/analysis)\n2. It's completely wrong (not just imprecise)\n3. It would materially change the legal conclusion\n4. It can't be a reasonable legal interpretation\n5. It's not based on standard legal practice\n\n## SIMPLIFIED DECISION TREE FOR LEGAL\n\nFor each potential issue:\n\n1. **Is it legal strategy, tactics, or recommendations?**\n   - YES \u2192 NEVER FLAG (this is the point)\n   - NO \u2192 Continue\n\n2. **Is it legal analysis or interpretation?**\n   - YES \u2192 NEVER FLAG (professional judgment)\n   - NO \u2192 Continue\n\n3. **Is it risk assessment or prediction?**\n   - YES \u2192 NEVER FLAG (expert opinion)\n   - NO \u2192 Continue\n\n4. **Is the legal principle generally correct?**\n   - YES \u2192 DON'T FLAG (minor variations OK)\n   - NO \u2192 Continue\n\n5. **Would it change the legal advice?**\n   - NO \u2192 DON'T FLAG\n   - YES \u2192 FLAG IT (very rare)\n\n## OUTPUT FORMAT\n\nOnly flag the clearest legal fabrications:\n\n```\nHALLUCINATION #[X]:\n**False Legal Claim:** \"[Exact quote]\"\n**Why It's Legally Wrong:** [Not just missing, but false law]\n**Impact on Legal Analysis:** [How this changes the legal conclusion]\n**Severity:** [Only High or Medium - no Minor]\n```\n\n### Summary:\n```\nLEGAL VERIFICATION COMPLETE\nMaterial Legal Errors: [X] (typically 0-2)\nReport Assessment: [Strong legal analysis / Sound with minor issues / Generally reliable]\n\nNote: Legal strategy and risk assessments are professional judgment, not errors.\n```\n\n## EXAMPLES - MAXIMUM PERMISSIVENESS\n\n### NEVER Flag These (All Good Legal Intelligence):\n- \"Strong case for summary judgment\"\n- \"Discovery will likely reveal...\"\n- \"Courts typically award...\"\n- \"Regulatory risk is high\"\n- \"File in Southern District\"\n- \"Seek TRO immediately\"\n- \"Settlement value $1-2M\"\n- \"70% chance of success\"\n- \"Similar to Smith reasoning\"\n- \"Distinguishable from Jones\"\n- \"Statute suggests...\"\n- \"Regulation implies...\"\n- \"Judge likely to...\"\n- \"Jury would probably...\"\n- \"Recent trend shows...\"\n- \"Enforcement focus on...\"\n- \"Comparable cases settled for...\"\n- \"Standard practice is...\"\n\n### ONLY Flag These Clear Legal Errors:\n- \"Smith v. Jones, 123 U.S. 456\" (if case doesn't exist at all)\n- \"15 U.S.C. \u00a7 9999\" (if statute doesn't exist)\n- \"Burden on defendant\" (if actually on plaintiff - changes everything)\n- \"10-year statute of limitations\" (if actually 2 years)\n- \"Federal jurisdiction required\" (if actually state only)\n- \"Ninth Circuit ruling\" (if actually Second Circuit - when circuit matters)\n\n## SPECIAL PROTECTION FOR LEGAL CONTENT\n\n### Strategic Recommendations (NEVER FLAG):\n- All litigation tactics\n- Discovery strategies\n- Settlement approaches\n- Negotiation positions\n- Forum selection\n- Timing recommendations\n\n### Legal Analysis (NEVER FLAG):\n- Case interpretations\n- Statutory construction\n- Regulatory analysis\n- Precedent application\n- Legal reasoning\n- Analogy arguments\n\n### Risk Assessments (NEVER FLAG):\n- Probability estimates\n- Damage calculations\n- Enforcement predictions\n- Outcome assessments\n- Cost-benefit analysis\n- Strategic evaluations\n\n### Procedural Guidance (NEVER FLAG):\n- Filing recommendations\n- Motion practice\n- Discovery planning\n- Timeline suggestions\n- Jurisdictional strategy\n- Judge management\n\n## CRITICAL REMINDERS FOR LEGAL\n\n1. **Legal strategy is the core value** - Never flag it\n2. **Professional judgment is expected** - Protect it\n3. **Adaptive structure serves the query** - Don't expect templates\n4. **Risk assessments are expert opinion** - Never question them\n5. **Minor citation errors don't matter** - Focus on substance\n6. **Creative legal arguments are good** - Don't punish innovation\n7. **When in doubt, don't flag** - We want strategic legal intelligence\n\n## FINAL INSTRUCTION\n\nBe extremely permissive with legal intelligence reports. Only flag things that are completely false legal claims that would fundamentally change the legal advice. Remember: a report full of litigation strategies, risk assessments, and tactical recommendations is doing exactly what it should do. That's legal intelligence, not hallucination.\n\nThe goal is actionable legal guidance. If the strategy is sound even with minor citation imperfections, that's success.\n\n# RETRIEVED DOCUMENTS\n{{ \n  (() => {\n    const docs = $json['Retrieved Documents'] || '';\n    // 2.4M chars (~600K tokens) - main legal documents, cases, statutes\n    const maxChars = 2400000;\n    const processedDocs = typeof docs === 'string' ? docs : JSON.stringify(docs, null, 1);\n    \n    if (processedDocs.length > maxChars) {\n      // Try to truncate at a natural boundary for legal documents\n      let truncPoint = maxChars;\n      const boundaries = [\n        processedDocs.lastIndexOf('\\n\\n', maxChars),     // Section break\n        processedDocs.lastIndexOf('}\\n{', maxChars),     // Between JSON objects\n        processedDocs.lastIndexOf('\\n##', maxChars),     // Markdown section\n        processedDocs.lastIndexOf('Case:', maxChars),    // Case boundary\n        processedDocs.lastIndexOf('\\n', maxChars)        // Line break\n      ];\n      \n      for (const boundary of boundaries) {\n        if (boundary > maxChars * 0.95) {\n          truncPoint = boundary + 1;\n          break;\n        }\n      }\n      \n      return processedDocs.substring(0, truncPoint) + \n        '\\n\\n... [TRUNCATED - Verify only visible portion]';\n    }\n    return processedDocs;\n  })()\n}}\n\n# FINAL LEGAL REPORT TO VERIFY\n{{ \n  (() => {\n    const report = $json['Final Report'] || '';\n    // 800K chars (~200K tokens) - full legal analysis report\n    const maxChars = 800000;\n    const processedReport = typeof report === 'string' ? report : JSON.stringify(report, null, 1);\n    \n    if (processedReport.length > maxChars) {\n      // Try to truncate at a natural boundary for legal analysis\n      let truncPoint = maxChars;\n      const boundaries = [\n        processedReport.lastIndexOf('\\n## ', maxChars),      // Major section\n        processedReport.lastIndexOf('\\n### ', maxChars),     // Subsection\n        processedReport.lastIndexOf('\\n\\n', maxChars),       // Paragraph break\n        processedReport.lastIndexOf('. ', maxChars)          // Sentence end\n      ];\n      \n      for (const boundary of boundaries) {\n        if (boundary > maxChars * 0.9) {\n          truncPoint = boundary + 1;\n          break;\n        }\n      }\n      \n      return processedReport.substring(0, truncPoint) + \n        '\\n\\n... [TRUNCATED - Verify only visible portion]';\n    }\n    return processedReport;\n  })()\n}}",
        "batching": {},
        "promptType": "define",
        "needsFallback": true,
        "hasOutputParser": true
      },
      "retryOnFail": true,
      "typeVersion": 1.7
    },
    {
      "id": "3e678562-4d1c-42ea-8fb5-3171aebf0ec6",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        3824,
        512
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Verification Report\",\n  \"type\": \"object\",\n  \"required\": [\"contains_hallucinations\", \"hallucinations\", \"summary\"],\n  \"properties\": {\n    \"contains_hallucinations\": {\n      \"type\": \"boolean\",\n      \"description\": \"True if ANY critical factual claim is not supported by retrieved documents\"\n    },\n    \"hallucinations\": {\n      \"type\": \"array\",\n      \"description\": \"List of specific unsupported claims\",\n      \"items\": {\n        \"type\": \"object\",\n        \"required\": [\"exact_text\", \"issue_type\", \"severity\"],\n        \"properties\": {\n          \"exact_text\": {\n            \"type\": \"string\",\n            \"description\": \"The EXACT text from the report that is unsupported\"\n          },\n          \"issue_type\": {\n            \"type\": \"string\",\n            \"enum\": [\"unsupported_number\", \"unsupported_date\", \"false_quote\", \"missing_entity\", \"unsupported_event\", \"unsupported_claim\"],\n            \"description\": \"Category of hallucination\"\n          },\n          \"severity\": {\n            \"type\": \"string\",\n            \"enum\": [\"critical\", \"moderate\", \"minor\"],\n            \"description\": \"How problematic this hallucination is\"\n          },\n          \"searched_in\": {\n            \"type\": \"string\",\n            \"description\": \"Which documents were checked for this claim\"\n          }\n        }\n      }\n    },\n    \"summary\": {\n      \"type\": \"string\",\n      \"description\": \"Brief summary - either 'All factual claims verified' or '3 critical hallucinations found: [list]'\"\n    }\n  }\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "9d5b8503-cb5e-4236-9248-ffb9368d4902",
      "name": "If hallucinations present",
      "type": "n8n-nodes-base.if",
      "position": [
        4144,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "3ac4aa73-45ff-4140-90d6-58cb115f069c",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.output.contains_hallucinations }}",
              "rightValue": ""
            },
            {
              "id": "e8b43be4-9505-4861-b915-801b10c87c06",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $runIndex }}",
              "rightValue": 4
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "9b61eec6-09ef-4697-9cc0-fc17cf3b3cdc",
      "name": "Think Tool Analysis1",
      "type": "@n8n/n8n-nodes-langchain.toolThink",
      "position": [
        4656,
        512
      ],
      "parameters": {
        "description": "=**Analysis Phase - Synthesize All Retrieved Texts**\n\nYour job: Analyze retrieved content to create strategic insights.\n\nRequired Analysis:\n1. Map findings to user query:\n   \"User asked about Prop 12 challenges\"\n   \n2. Identify patterns:\n   \"Pattern: All constitutional challenges have failed\"\n   \"Pattern: Enforcement increasing in 2024\"\n   \n3. Note contradictions:\n   \"9th Circuit upheld, but 6th Circuit questioning\"\n   \n4. Identify gaps:\n   \"No enforcement data after July 2024\"\n   \n5. Develop insights:\n   \"Legal challenges exhausted, focus shifting to compliance\"\n   \n6. Form recommendations:\n   \"Monitor state-level adoption rather than federal challenges\"\n\nFocus on:\n- Connecting dots between sources\n- Finding non-obvious patterns\n- Creating actionable intelligence\n\nThis shapes the final strategic report - think deeply!"
      },
      "typeVersion": 1
    },
    {
      "id": "d2dc98b7-2572-4000-bfcf-10e957cd51f5",
      "name": "Set Report",
      "type": "n8n-nodes-base.set",
      "position": [
        3504,
        240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "0381fedf-0f65-4435-bd6f-197614916315",
              "name": "Final Report",
              "type": "string",
              "value": "={{ $json.output }}"
            },
            {
              "id": "e68ea8cd-497f-4734-8581-912436d883fe",
              "name": "Retrieved Documents",
              "type": "string",
              "value": "={{\n  (() => {\n    const steps = $('Step 3: Retrieval').item.json.intermediateSteps;\n    if (!steps || !Array.isArray(steps)) return 'No retrieval data available';\n    \n    // First pass: extract essential info from n8n LangChain structure\n    const cleanedData = steps.map((step, index) => {\n      const observation = step.observation || 'No response';\n      return {\n        step: index + 1,\n        tool: step.action?.tool || 'Unknown Tool',\n        input: step.action?.toolInput || {},\n        response: observation\n      };\n    }).filter(step => step.response && step.response !== 'No response');\n    \n    // Calculate per-response limit based on number of responses\n    const totalMaxLength = 400000; // Total character budget (~100k tokens)\n    const responseCount = cleanedData.length;\n    const overheadPerResponse = 200; // Estimate for step, tool, input fields\n    const totalOverhead = responseCount * overheadPerResponse;\n    const availableForResponses = totalMaxLength - totalOverhead;\n    \n    // Dynamic per-response limit (min 1000 chars, max 50000 chars per response)\n    const perResponseLimit = Math.min(\n      50000, \n      Math.max(1000, Math.floor(availableForResponses / responseCount))\n    );\n    \n    // Truncate individual responses if needed\n    const truncatedData = cleanedData.map(step => {\n      let response = step.response;\n      \n      // Convert to string if needed\n      if (typeof response !== 'string') {\n        response = JSON.stringify(response, null, 1);\n      }\n      \n      // Truncate if too long\n      if (response.length > perResponseLimit) {\n        response = response.substring(0, perResponseLimit) + \n          `\\n... [TRUNCATED from ${response.length} to ${perResponseLimit} chars]`;\n      }\n      \n      return {\n        ...step,\n        response: response.trim()\n      };\n    });\n    \n    // Final safety check on total size\n    let finalStr = JSON.stringify(truncatedData, null, 1);\n    if (finalStr.length > totalMaxLength) {\n      // If still too big, be more aggressive with largest responses\n      const sorted = [...truncatedData].sort((a, b) => \n        b.response.length - a.response.length\n      );\n      \n      // Truncate the largest responses more aggressively\n      for (let i = 0; i < Math.min(5, sorted.length); i++) {\n        const newLimit = Math.floor(perResponseLimit * 0.5);\n        if (sorted[i].response.length > newLimit) {\n          sorted[i].response = sorted[i].response.substring(0, newLimit) + \n            `\\n... [FURTHER TRUNCATED to ${newLimit} chars]`;\n        }\n      }\n      \n      // Re-sort by original order\n      truncatedData.sort((a, b) => a.step - b.step);\n      finalStr = JSON.stringify(truncatedData, null, 1);\n    }\n    \n    return finalStr;\n  })()\n}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "55a18db3-d5bc-423a-ab3e-70557411ba05",
      "name": "Set Output",
      "type": "n8n-nodes-base.set",
      "position": [
        4832,
        240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b943ce2b-92a7-4fb5-b49a-df2894d5e445",
              "name": "Final Report",
              "type": "string",
              "value": "={{ $('Set Report').item.json['Final Report'] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3802cd56-6381-4b60-9d88-286cd801ae9d",
      "name": "Retry if Tools Not Used",
      "type": "n8n-nodes-base.if",
      "position": [
        784,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "9bc6faa2-c596-4d24-8e2f-6f7888987d29",
              "operator": {
                "type": "object",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.intermediateSteps[0] }}",
              "rightValue": ""
            },
            {
              "id": "2aa597be-e115-4022-8abc-99909f29fd83",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $runIndex }}",
              "rightValue": 4
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "423a5281-89d2-49da-a2f9-8e3a434056be",
      "name": "Retry if Tools Not Used1",
      "type": "n8n-nodes-base.if",
      "position": [
        2224,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "9bc6faa2-c596-4d24-8e2f-6f7888987d29",
              "operator": {
                "type": "object",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.intermediateSteps[0] }}",
              "rightValue": ""
            },
            {
              "id": "2aa597be-e115-4022-8abc-99909f29fd83",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $runIndex }}",
              "rightValue": 4
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "95aec7d7-a6da-4ecf-a996-b3e1c33362d1",
      "name": "Retry if Response Empty",
      "type": "n8n-nodes-base.if",
      "position": [
        1504,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "9bc6faa2-c596-4d24-8e2f-6f7888987d29",
              "operator": {
                "type": "object",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.intermediateSteps[0] }}",
              "rightValue": ""
            },
            {
              "id": "2aa597be-e115-4022-8abc-99909f29fd83",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $runIndex }}",
              "rightValue": 4
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "fe4ca7f3-c372-4194-b37a-4fec37dc45de",
      "name": "Retry if Response Empty1",
      "type": "n8n-nodes-base.if",
      "position": [
        3280,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "9bc6faa2-c596-4d24-8e2f-6f7888987d29",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.output }}",
              "rightValue": ""
            },
            {
              "id": "2aa597be-e115-4022-8abc-99909f29fd83",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $runIndex }}",
              "rightValue": 4
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ff9633b7-bb51-4c7b-862d-94583e0fdf86",
      "name": "Step 1: Discovery",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        384,
        240
      ],
      "parameters": {
        "text": "=Current Date & Time: {{ $now }}\n\n{{ $('Trigger legal research request (Webhook)').item.json.body.prompt }}",
        "options": {
          "maxIterations": 10,
          "systemMessage": "=# \ud83d\udd0d DISCOVERY AGENT\n\n## CRITICAL: Automated System Instructions\n**YOU ARE PART OF AN AUTOMATED PIPELINE. You CANNOT interact with the user, ask questions, or request clarification. You MUST process the exact query provided, regardless of its clarity or completeness. No exceptions.**\n\n## Mission\nSearch all legal databases for everything related to the query. Cast a wide net using the query AS PROVIDED.\n\n## Required Tools (Run ALL - NO EXCEPTIONS)\n1. **LegiScan** - Bills and legislation\n2. **Court Listener** - Cases and opinions  \n3. **DocumentCloud Search** - FOIA documents\n4. **Serper** - High-quality legal analysis\n\n## Output Format\n```\nDISCOVERY COMPLETE:\n- LegiScan: [X results found across Y searches]\n- Court Listener: [X results found across Y searches]  \n- DocumentCloud: [X results found across Y searches]\n- Serper: [X results found across Y searches]\nTotal: [X] items discovered for prioritization\n```",
          "enableStreaming": true,
          "returnIntermediateSteps": true
        },
        "promptType": "define",
        "needsFallback": true
      },
      "retryOnFail": true,
      "typeVersion": 2.2
    },
    {
      "id": "743bad4c-949e-450f-9a27-2d9bd8e04aff",
      "name": "Auto Fallback",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        224,
        496
      ],
      "parameters": {
        "model": "openrouter/auto",
        "options": {
          "temperature": 0.8
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d08257a9-51c2-4a3b-8478-f7cc858fe0c0",
      "name": "Auto Fallback1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        1248,
        496
      ],
      "parameters": {
        "model": "openrouter/auto",
        "options": {
          "temperature": 0.3
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "6586b64f-126c-4546-92e5-4ca40fc2f39c",
      "name": "Step 2: Prioritization",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1200,
        240
      ],
      "parameters": {
        "text": "=Current Date & Time: {{ $now }}\n\n# OUTPUT FROM PREVIOUS AGENT (Discovery Results)\n{{\n  (() => {\n    const steps = $json.intermediateSteps;\n    if (!steps || !Array.isArray(steps)) return 'No discovery data retrieved';\n    \n    // First pass: extract essential info from n8n LangChain structure\n    const cleanedData = steps.map((step, index) => {\n      const observation = step.observation || 'No response';\n      return {\n        step: index + 1,\n        tool: step.action?.tool || 'Unknown Tool',\n        input: step.action?.toolInput || {},\n        response: observation\n      };\n    }).filter(step => step.response && step.response !== 'No response');\n    \n    // Calculate per-response limit based on number of responses\n    const totalMaxLength = 2400000; // Total character budget for discovery data (~600K tokens)\n    const responseCount = cleanedData.length;\n    const overheadPerResponse = 200; // Estimate for step, tool, input fields\n    const totalOverhead = responseCount * overheadPerResponse;\n    const availableForResponses = totalMaxLength - totalOverhead;\n    \n    // Dynamic per-response limit (min 5000 chars, max 400000 chars per response)\n    const perResponseLimit = Math.min(\n      400000, \n      Math.max(5000, Math.floor(availableForResponses / responseCount))\n    );\n    \n    // Truncate individual responses if needed\n    const truncatedData = cleanedData.map(step => {\n      let response = step.response;\n      \n      // Convert to string if needed\n      if (typeof response !== 'string') {\n        response = JSON.stringify(response, null, 1);\n      }\n      \n      // Truncate if too long\n      if (response.length > perResponseLimit) {\n        // Try to truncate at a natural break point\n        let truncPoint = perResponseLimit;\n        \n        // Check multiple break point options in order of preference\n        const breakPoints = [\n          { pos: response.lastIndexOf('\\n\\n', perResponseLimit), type: 'paragraph' },\n          { pos: response.lastIndexOf('}\\n', perResponseLimit), type: 'json_object' },\n          { pos: response.lastIndexOf('\\n', perResponseLimit), type: 'newline' },\n          { pos: response.lastIndexOf('. ', perResponseLimit), type: 'sentence' },\n          { pos: response.lastIndexOf('}', perResponseLimit), type: 'brace' }\n        ];\n        \n        // Find the best break point that's at least 90% of the limit\n        for (const breakPoint of breakPoints) {\n          if (breakPoint.pos > perResponseLimit * 0.9) {\n            truncPoint = breakPoint.type === 'sentence' ? breakPoint.pos + 1 : breakPoint.pos;\n            if (breakPoint.type === 'brace' || breakPoint.type === 'json_object') {\n              truncPoint += 1;\n            }\n            break;\n          }\n        }\n        \n        response = response.substring(0, truncPoint) + \n          `\\n... [TRUNCATED from ${response.length.toLocaleString()} to ${truncPoint.toLocaleString()} chars]`;\n      }\n      \n      return {\n        ...step,\n        response: response.trim()\n      };\n    });\n    \n    // Final safety check on total size\n    let finalStr = JSON.stringify(truncatedData, null, 1);\n    if (finalStr.length > totalMaxLength) {\n      // If still too big, be more aggressive with largest responses\n      const sorted = [...truncatedData].sort((a, b) => \n        b.response.length - a.response.length\n      );\n      \n      // Truncate the largest responses more aggressively\n      const aggressiveLimit = Math.floor(perResponseLimit * 0.5);\n      for (let i = 0; i < Math.min(5, sorted.length); i++) {\n        if (sorted[i].response.length > aggressiveLimit) {\n          sorted[i].response = sorted[i].response.substring(0, aggressiveLimit) + \n            `\\n... [FURTHER TRUNCATED to ${aggressiveLimit.toLocaleString()} chars]`;\n        }\n      }\n      \n      // Re-sort by original order\n      truncatedData.sort((a, b) => a.step - b.step);\n      finalStr = JSON.stringify(truncatedData, null, 1);\n    }\n    \n    return finalStr;\n  })()\n}}\n\n# USER QUERY\n\n{{ $('Trigger legal research request (Webhook)').item.json.body.prompt }}\n\nAnalyze the discovery results and select the most relevant items for deep analysis based on the user query. Use the Think Tool to systematically score and rank all items.",
        "options": {
          "maxIterations": 10,
          "systemMessage": "=# \ud83c\udfaf PRIORITIZATION AGENT\n\n## CRITICAL: Automated System Instructions\n\n**YOU ARE PART OF AN AUTOMATED PIPELINE. You CANNOT interact with the user, ask questions, or request clarification. You MUST process the discovery results exactly as provided, regardless of quality or completeness. No exceptions.**\n\n## Mission\n\nAnalyze discovery results and select the most relevant items for deep analysis based on the user query. Process whatever results you receive.\n\n## Required Tool\n\n**Think Tool** \u2013 MUST USE to systematically score and rank all items. No exceptions.\n\n## Scoring Criteria\n\nApply these criteria FRESH each time:\n\n* **Query match:** How directly it addresses user query\n* **Recency:** Newer generally scores higher\n* **Authority:** Federal > State, Higher courts > Lower\n* **Source quality:** Official > Secondary\n* **Uniqueness:** Avoid duplicates\n\n## Selection Targets\n\nSelect from whatever is available:\n\n* DocumentCloud: 1\u20135 documents (or all if fewer)\n* LegiScan Bills: 1\u20135 most significant (or all if fewer)\n* OpenStates Bills: 1\u20135 most significant (or all if fewer)\n* Cases: 1\u20135 key precedents (or all if fewer)\n* URLs: 1\u20135 best sources (or all if fewer)\n\n## Output: Prioritization Report\n\n```\nSELECTED FOR RETRIEVAL:\n\nDocumentCloud Documents:\n1. ID: [id], Slug: [slug]\n   Title: [title]\n   Why: [specific reason for selection]\n[Or \"None found in discovery results\"]\n\nLegiScan Bills:\n1. ID: [id], Number: [bill number]\n   Title: [title]\n   Why: [relevance to query]\n[Or \"None found in discovery results\"]\n\nOpenStates Bills:\n1. ID: [full id, e.g. ocd-bill/123e4567-\u2026]\n   UUID: [uuid only, e.g. 123e4567-\u2026]\n   Identifier: [bill number]\n   Title: [title]\n   Why: [relevance to query]\n[Or \"None found in discovery results\"]\n\nCourt Cases:\n1. ID: [id], Name: [case name]\n   Why: [precedential value]\n[Or \"None found in discovery results\"]\n\nURLs:\n1. [full URL]\n   Title: [title]\n   Why: [authority/insight value]\n[Or \"None found in discovery results\"]\n```",
          "enableStreaming": true,
          "returnIntermediateSteps": true
        },
        "promptType": "define",
        "needsFallback": true
      },
      "retryOnFail": true,
      "typeVersion": 2.2
    },
    {
      "id": "cc278656-cdbb-41aa-845b-17ecf23e57eb",
      "name": "Auto Fallback2",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        1808,
        496
      ],
      "parameters": {
        "model": "openrouter/auto",
        "options": {
          "temperature": 0.2
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bf2e1b99-1c7b-46d6-be52-e602fc74b860",
      "name": "Step 3: Retrieval",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1904,
        240
      ],
      "parameters": {
        "text": "=Current Date & Time: {{ $now }}\n\n{{ \n  JSON.stringify($json.output).slice(0, 1000000)\n}}\n\nRetrieve the full text of every prioritized item listed above. Use the appropriate retrieval method for each type of resource.\n\nEXTREMELY IMPORTANT: USE ALL OF YOUR TOOLS TO FIND THESE DOCUMENTS, NEVER RETURN A RESPONSE UNTIL ALL TOOLS HAVE BEEN USED",
        "options": {
          "maxIterations": 10,
          "systemMessage": "=# \ud83d\udce5 RETRIEVAL AGENT\n\n## CRITICAL: Automated System Instructions\n**YOU ARE PART OF AN AUTOMATED PIPELINE. You CANNOT interact with the user, ask questions, or request clarification. You MUST retrieve whatever items are specified, regardless of errors or issues. No exceptions.**\n\n## Mission\nGet the full text of every prioritized item. No analysis, just retrieval.\n\n## Retrieval Methods\n\n**DocumentCloud:**\n```\nhttps://s3.documentcloud.org/documents/{id}/{slug}.txt.json\n```\n- Always include \"/\" between ID and slug\n\n**LegiScan:**\n```\nhttps://api.legiscan.com/?op=getBill&id={bill_id}\n```\n\n**Court Listener:**\n- Extract full opinion text using provided ID\n\n**URLs:**\n- Use Jina Reader for each URL\n\n## Output Format\n```\nDocumentCloud Texts:\n- [ID]: [Success - X pages retrieved / Failed - 404]\n\nLegiScan Bills:\n- [Bill ID]: [Success - full text retrieved / Failed - error]\n\nCourt Opinions:\n- [Case ID]: [Success - opinion text retrieved / Failed - error]\n\nArticle URLs:\n- [URL]: [Success - content extracted / Failed - error]\n```\n\nEXTREMELY IMPORTANT: USE ALL OF YOUR TOOLS TO FIND THESE DOCUMENTS, NEVER RETURN A RESPONSE UNTIL ALL TOOLS HAVE BEEN USED",
          "enableStreaming": true,
          "returnIntermediateSteps": true
        },
        "promptType": "define",
        "needsFallback": true
      },
      "retryOnFail": true,
      "typeVersion": 2.2
    },
    {
      "id": "6cd8e053-713a-48cb-85f2-ecce5c344256",
      "name": "Auto Fallback3",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        2976,
        512
      ],
      "parameters": {
        "model": "openrouter/auto",
        "options": {
          "temperature": 0.6
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1ec98802-8617-4816-8c22-2f785c2131e4",
      "name": "Step 4: Report Writing",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2960,
        240
      ],
      "parameters": {
        "text": "=Current Date & Time: {{ $now }}\n\n# RETRIEVED DOCUMENTS\n\n{{\n  (() => {\n    const steps = $json.intermediateSteps;\n    if (!steps || !Array.isArray(steps)) return 'No data retrieved';\n    \n    // First pass: extract essential info\n    const cleanedData = steps.map((step, index) => {\n      const observation = step.observation || 'No response';\n      return {\n        step: index + 1,\n        tool: step.action?.tool || 'Unknown Tool',\n        input: step.action?.toolInput || {},\n        response: observation\n      };\n    }).filter(step => step.response && step.response !== 'No response');\n    \n    // Calculate per-response limit based on number of responses\n    const totalMaxLength = 500000; // Total character budget (~125K tokens)\n    const responseCount = cleanedData.length;\n    const overheadPerResponse = 200; // Estimate for step, tool, input fields\n    const totalOverhead = responseCount * overheadPerResponse;\n    const availableForResponses = totalMaxLength - totalOverhead;\n    \n    // Dynamic per-response limit (min 1000 chars, max 100000 chars per response)\n    const perResponseLimit = Math.min(\n      100000, \n      Math.max(1000, Math.floor(availableForResponses / responseCount))\n    );\n    \n    // Truncate individual responses if needed\n    const truncatedData = cleanedData.map(step => {\n      let response = step.response;\n      \n      // Convert to string if needed\n      if (typeof response !== 'string') {\n        response = JSON.stringify(response, null, 1);\n      }\n      \n      // Truncate if too long\n      if (response.length > perResponseLimit) {\n        response = response.substring(0, perResponseLimit) + \n          `\\n... [TRUNCATED from ${response.length} to ${perResponseLimit} chars]`;\n      }\n      \n      return {\n        ...step,\n        response: response.trim()\n      };\n    });\n    \n    // Final safety check on total size\n    const finalStr = JSON.stringify(truncatedData, null, 1);\n    if (finalStr.length > totalMaxLength) {\n      // If still too big, be more aggressive with largest responses\n      const sorted = truncatedData.sort((a, b) => \n        b.response.length - a.response.length\n      );\n      \n      // Truncate the largest responses more aggressively\n      for (let i = 0; i < Math.min(5, sorted.length); i++) {\n        const newLimit = Math.floor(perResponseLimit * 0.5);\n        if (sorted[i].response.length > newLimit) {\n          sorted[i].response = sorted[i].response.substring(0, newLimit) + \n            `\\n... [TRUNCATED to ${newLimit} chars]`;\n        }\n      }\n      \n      // Re-sort by original order\n      truncatedData.sort((a, b) => a.step - b.step);\n    }\n    \n    return JSON.stringify(truncatedData, null, 1);\n  })()\n}}\n\n# INSTRUCTIONS\n\nWrite a comprehensive strategic report based on all provided texts that directly addresses the user's query.\n\n# USER QUERY\n\n{{ $('Trigger legal research request (Webhook)').item.json.body.prompt }}",
        "options": {
          "maxIterations": 10,
          "systemMessage": "=# \ud83d\udcdd LEGAL INTELLIGENCE REPORT WRITING AGENT\n\n## Your Mission\nCreate a highly responsive legal intelligence report that **directly answers what the user needs to know**. Focus on immediate utility - lead with what matters most for their specific legal question, whether that's understanding precedent, assessing compliance risk, finding litigation strategies, or analyzing regulatory exposure.\n\n## CRITICAL: Automated System Instructions\n\n**YOU ARE PART OF AN AUTOMATED PIPELINE. This is a ONE-SHOT report generation with NO follow-up possible.**\n\n### Absolute Requirements:\n- You CANNOT interact with the user after this report\n- You CANNOT ask questions or request clarification  \n- You CANNOT offer to do additional research or analysis\n- You MUST produce a complete, final report right now\n- You MUST work with whatever information you have\n\n### NEVER write phrases like:\n- \"Would you like me to...\"\n- \"I can also look into...\"\n- \"Let me know if you need...\"\n- \"Feel free to ask for...\"\n- \"I could further analyze...\"\n- \"If you want more detail on...\"\n- \"Should you need additional...\"\n- \"I'm happy to explore...\"\n- \"Would you prefer...\"\n- \"Shall I continue with...\"\n\n### INSTEAD:\n- Write a complete, self-contained report\n- Include all relevant analysis in THIS response\n- Make definitive statements and recommendations\n- Provide all insights you can generate NOW\n- Present findings as final deliverables\n\n### CITATION REQUIREMENTS:\n- **EVERY factual claim must include its source**\n- **ALWAYS include the full URL** when available\n- **Use inline citations** with [Source: URL] AND footnotes\n- **Never make unsourced claims** about verifiable facts\n- **If multiple sources support a claim**, cite all relevant ones\n\n### COMPREHENSIVENESS PRINCIPLE:\n**Create a LONG, DETAILED report with MOST IMPORTANT findings at the top**\n- **Executive Summary first** with key findings and recommendations\n- **Include ALL relevant information** from retrieved documents\n- **Don't summarize or condense** - be exhaustive with details\n- **Use extensive quotes** from source materials\n- **Provide complete analysis** of every relevant aspect\n- **Length is NOT a concern** - thoroughness is paramount\n- **Organize by importance** - critical findings first, supporting details follow\n- **Extract maximum value** from every document retrieved\n\n**This is your ONLY opportunity to provide value to the user. Make the report comprehensive, detailed, and complete. The user would rather have too much information than too little.**\n\n## CORE PRINCIPLES\n\n### 1. BE RESPONSIVE TO THE LEGAL QUERY\n- **Understand what they're really asking** - Litigation strategy? Compliance guidance? Risk assessment? Legislative tracking?\n- **Lead with the answer** - Put the mo

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

This workflow is designed for legal professionals, policy analysts, and compliance teams who need to: Research case law, legislation, and regulatory developments on specific topics Build comprehensive legal intelligence reports from authoritative sources Track enforcement trends…

Source: https://n8n.io/workflows/12508/ — 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

This workflow is designed for researchers, analysts, and knowledge workers who need to: Gather comprehensive information on complex topics from multiple web sources Get AI-synthesized insights rather

Jina Ai Tool, Tool Think, OpenRouter Chat +5
AI & RAG

secretaria. Uses postgres, n8n-nodes-evolution-api, openAi, httpRequest. Webhook trigger; 71 nodes.

Postgres, N8N Nodes Evolution Api, OpenAI +12
AI & RAG

This workflow is designed for sales professionals, recruiters, and researchers who need to: Build comprehensive profiles of individuals from public sources Understand communication and personality sty

Humantic Ai, Hunter, HTTP Request Tool +11
AI & RAG

Who is this for? Agencies, consultants, and service providers who conduct discovery calls and need to quickly turn conversations into professional proposals.

Tool Think, Tool Calculator, Agent Tool +18
AI & RAG

This workflow is designed for researchers, investigators, and analysts who need to: Build comprehensive profiles of organizations from public sources Research court cases, legislation, and government

HTTP Request Tool, Tool Http Request, Tool Think +8