{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "8a78e62f-28c0-4d24-9a33-521acd836974",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2032,
        1552
      ],
      "parameters": {
        "color": 5,
        "width": 560,
        "height": 520,
        "content": "## Enterprise AI Contract Intelligence - Pro\n\n**Who it's for:** Legal, Procurement, Compliance teams. ESN/agencies.\n\n**What it does:**\n1. Upload contract PDF via form (with metadata: type, department, counterparty)\n2. Deduplication check against Supabase\n3. AI Pass 1 - Classification (parties, dates, jurisdiction, contract type)\n4. AI Pass 2 - Deep risk analysis (clauses, red flags, obligations, recommendations)\n5. Build structured report (Slack Blocks + HTML email)\n6. Store in Supabase with full metadata\n7. Slack Block Kit alert (high risk) or summary (low risk)\n8. Gmail HTML report to submitter (configurable)\n9. Error handling with admin Slack channel\n\n**Setup:** Run the SQL in the \"Supabase Schema\" sticky. Add credentials: OpenAI (Header Auth), Supabase, Slack, Gmail. Configure the Config node."
      },
      "typeVersion": 1
    },
    {
      "id": "54d26b31-109f-4508-aaeb-0ecbd3bea3d6",
      "name": "Supabase Schema",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2032,
        896
      ],
      "parameters": {
        "color": 6,
        "width": 564,
        "height": 620,
        "content": "**Supabase Schema** \u2014 Run in Supabase SQL Editor before use:\n\n```sql\nCREATE TABLE IF NOT EXISTS contract_analyses (\n  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,\n  filename TEXT NOT NULL,\n  contract_type TEXT,\n  jurisdiction TEXT,\n  submitter_name TEXT,\n  submitter_email TEXT,\n  department TEXT,\n  counterparty TEXT,\n  raw_text_preview TEXT,\n  parties JSONB,\n  key_clauses JSONB,\n  overall_risk_score INT DEFAULT 0,\n  risk_level TEXT DEFAULT 'unknown',\n  executive_summary TEXT,\n  top_risks JSONB,\n  recommendations JSONB,\n  classification JSONB,\n  status TEXT DEFAULT 'analyzed'\n    CHECK (status IN ('analyzed','flagged','archived','reviewing')),\n  analyzed_at TIMESTAMPTZ DEFAULT NOW(),\n  created_at TIMESTAMPTZ DEFAULT NOW()\n);\nCREATE INDEX IF NOT EXISTS idx_ca_score\n  ON contract_analyses(overall_risk_score DESC);\nCREATE INDEX IF NOT EXISTS idx_ca_status\n  ON contract_analyses(status);\nCREATE INDEX IF NOT EXISTS idx_ca_type\n  ON contract_analyses(contract_type);\nCREATE INDEX IF NOT EXISTS idx_ca_created\n  ON contract_analyses(created_at DESC);\nCREATE INDEX IF NOT EXISTS idx_ca_filename\n  ON contract_analyses(filename);\n```"
      },
      "typeVersion": 1
    },
    {
      "id": "87dfd4af-c94e-4d18-b51f-0b3c56cf7072",
      "name": "Step 1 Ingest",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2640,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 140,
        "content": "**Step 1 - Ingest**\n\nForm upload with metadata (contract type, submitter, department, counterparty) or Manual test with sample data. Config node centralizes all settings."
      },
      "typeVersion": 1
    },
    {
      "id": "e63d0723-0341-43a0-b15b-f48d78178052",
      "name": "Step 2 Deduplicate",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2976,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 140,
        "content": "**Step 2 - Deduplicate**\n\nQuery Supabase for existing analysis with the same filename. If found, skip analysis and show previous result. Avoids wasting AI tokens on re-uploads."
      },
      "typeVersion": 1
    },
    {
      "id": "f658564e-c7a3-4808-a59e-0815adbcceba",
      "name": "Step 3 Extract",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3312,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 140,
        "content": "**Step 3 - Extract & Prepare**\n\nExtract text from PDF binary, normalize, truncate to 12k chars, merge with form metadata and config for downstream AI calls."
      },
      "typeVersion": 1
    },
    {
      "id": "a8abaca7-fffe-4268-a7fd-4b8f54fd3086",
      "name": "Step 4 AI Analysis",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3696,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 340,
        "content": "**Step 4 - AI Two-Pass Analysis**\n\nPass 1: Quick classification (type, parties, dates, jurisdiction, ~300 tokens).\nPass 2: Deep risk analysis (clauses, obligations, red flags, recommendations, ~2000 tokens).\nSplit for cost efficiency and structured output."
      },
      "typeVersion": 1
    },
    {
      "id": "aa16c460-b7aa-4b7f-9a75-fd05b74287c9",
      "name": "Step 5 Report Alert",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4144,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "content": "**Step 5 - Report, Store & Alert**\n\nBuild Report generates Slack Block Kit JSON + HTML email body.\nSupabase insert stores full analysis with metadata.\nHigh risk \u2192 Slack Blocks alert + Gmail HTML report.\nLow risk \u2192 Slack summary notification.\nForm Ending shows completion to user."
      },
      "typeVersion": 1
    },
    {
      "id": "cb0e27e8-f26e-436d-a474-f10c699dccb1",
      "name": "Error Handling Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4832,
        1552
      ],
      "parameters": {
        "color": 2,
        "width": 320,
        "height": 120,
        "content": "**Error Handling**\n\nError Trigger catches any workflow failure and sends a notification to the admin Slack channel (#n8n-errors). Includes workflow name, error message, and timestamp."
      },
      "typeVersion": 1
    },
    {
      "id": "2dc1a40d-d75a-43c6-a19f-ac60f5b6a88a",
      "name": "Upload Contract",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        2736,
        1856
      ],
      "parameters": {
        "options": {},
        "formTitle": "Contract AI Analysis \u2014 Upload & Analyze",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "Contract PDF",
              "requiredField": true,
              "acceptFileTypes": ".pdf,.docx"
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Contract Type",
              "fieldOptions": {
                "values": [
                  {
                    "option": "NDA"
                  },
                  {
                    "option": "MSA"
                  },
                  {
                    "option": "SOW / Statement of Work"
                  },
                  {
                    "option": "Employment"
                  },
                  {
                    "option": "SaaS / License"
                  },
                  {
                    "option": "Consulting"
                  },
                  {
                    "option": "Partnership"
                  },
                  {
                    "option": "Other"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Contract Name",
              "placeholder": "e.g. NDA - Acme Corp Q1 2025",
              "requiredField": true
            },
            {
              "fieldLabel": "Counterparty Name",
              "placeholder": "e.g. Acme Corporation",
              "requiredField": true
            },
            {
              "fieldLabel": "Your Name",
              "placeholder": "e.g. John Doe",
              "requiredField": true
            },
            {
              "fieldType": "email",
              "fieldLabel": "Your Email",
              "placeholder": "user@example.com",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Department",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Legal"
                  },
                  {
                    "option": "Procurement"
                  },
                  {
                    "option": "Finance"
                  },
                  {
                    "option": "Sales"
                  },
                  {
                    "option": "Engineering"
                  },
                  {
                    "option": "HR"
                  },
                  {
                    "option": "Operations"
                  },
                  {
                    "option": "Other"
                  }
                ]
              }
            }
          ]
        },
        "formDescription": "Upload a contract PDF for AI-powered analysis. The system will extract key clauses, assess risk, and provide actionable recommendations."
      },
      "typeVersion": 2.2
    },
    {
      "id": "536c0506-43b9-4869-9e66-182864bf5d63",
      "name": "Manual Test",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        2736,
        2160
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5d3ff4a9-4d75-4f35-b767-84c8f7f680f7",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "position": [
        2736,
        2000
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "RISK_THRESHOLD",
              "type": "number",
              "value": 70
            },
            {
              "id": "2",
              "name": "SLACK_CHANNEL",
              "type": "string",
              "value": "#legal-alerts"
            },
            {
              "id": "3",
              "name": "ADMIN_SLACK_CHANNEL",
              "type": "string",
              "value": "#n8n-errors"
            },
            {
              "id": "4",
              "name": "AI_MODEL",
              "type": "string",
              "value": "gpt-4o-mini"
            },
            {
              "id": "5",
              "name": "ALERT_EMAIL",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "6",
              "name": "ENABLE_EMAIL",
              "type": "boolean",
              "value": true
            },
            {
              "id": "7",
              "name": "CONTRACT_LANG",
              "type": "string",
              "value": "en"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "5003aa1e-ea7e-46c0-8e1c-0a3844f2c25c",
      "name": "Check Duplicate",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        3040,
        2000
      ],
      "parameters": {
        "url": "={{ $env.SUPABASE_URL }}/rest/v1/contract_analyses",
        "options": {
          "response": {
            "response": {}
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "filename",
              "value": "=eq.{{ $json.filename ?? $json['Contract Name'] ?? 'unknown' }}"
            },
            {
              "name": "select",
              "value": "id,filename,overall_risk_score,status,analyzed_at"
            },
            {
              "name": "limit",
              "value": "1"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.SUPABASE_SERVICE_KEY }}"
            },
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "nodeCredentialType": "openAiApi"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a0749143-735f-4443-b85f-6748c8a36c7d",
      "name": "Already Exists?",
      "type": "n8n-nodes-base.if",
      "position": [
        3264,
        2000
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": true
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ Array.isArray($json) ? $json.length : 0 }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "d6f15504-861a-48c9-b078-4158d17bb5d7",
      "name": "Already Analyzed",
      "type": "n8n-nodes-base.form",
      "position": [
        3472,
        1920
      ],
      "parameters": {
        "options": {},
        "operation": "completion",
        "completionTitle": "Already Analyzed",
        "completionMessage": "This contract has already been analyzed. Check Supabase for the existing report."
      },
      "typeVersion": 2.4
    },
    {
      "id": "1a67b439-5764-4fce-a3ef-5f4f28f70211",
      "name": "Has File?",
      "type": "n8n-nodes-base.if",
      "position": [
        3472,
        2096
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": true
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ ($binary || {}).data !== undefined }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3352c639-8fbe-40c9-ae9b-30faa9c8b083",
      "name": "Extract PDF Text",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        3696,
        2016
      ],
      "parameters": {
        "options": {},
        "operation": "pdf"
      },
      "typeVersion": 1.1
    },
    {
      "id": "9b475384-b8d7-480a-a249-607a16d68cfb",
      "name": "Sample Data",
      "type": "n8n-nodes-base.set",
      "position": [
        3696,
        2192
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "contractText",
              "type": "string",
              "value": "SAMPLE CONTRACT FOR TESTING\n\nThis Master Service Agreement (MSA) is entered into as of January 15, 2025, by and between:\n\nParty A: TechCorp International Ltd., a company incorporated under the laws of France, with registered office at 42 Avenue des Champs-\u00c9lys\u00e9es, 75008 Paris (\"Client\")\n\nParty B: CloudVendor Solutions Inc., a Delaware corporation, with principal office at 100 Market Street, San Francisco, CA 94105 (\"Vendor\")\n\n1. TERM: This Agreement shall commence on the Effective Date and continue for a period of thirty-six (36) months, automatically renewing for successive 12-month periods unless terminated.\n\n2. TERMINATION: Either party may terminate with 30 days written notice. Client may terminate for convenience with 15 days notice. Early termination fee: 50% of remaining contract value.\n\n3. LIABILITY: Vendor's total aggregate liability shall not exceed the fees paid in the 12 months preceding the claim. EXCLUSION: This cap does NOT apply to breaches of confidentiality, IP infringement, or gross negligence.\n\n4. INDEMNIFICATION: Vendor shall indemnify, defend, and hold harmless Client against all third-party claims arising from: (a) IP infringement, (b) data breaches caused by Vendor negligence, (c) violation of applicable law. Client indemnifies Vendor against claims arising from Client's misuse of services.\n\n5. INTELLECTUAL PROPERTY: All pre-existing IP remains with original owner. Work product created specifically for Client shall be owned by Client upon full payment. Vendor retains license to use general methodologies and tools.\n\n6. CONFIDENTIALITY: 5-year obligation post-termination. Covers all non-public business and technical information. Standard exceptions apply (public domain, independent development, legal compulsion).\n\n7. DATA PROTECTION: Vendor shall comply with GDPR and applicable data protection laws. DPA attached as Annex B. Sub-processors require prior written consent.\n\n8. WARRANTY: Vendor warrants services will be performed in a professional manner consistent with industry standards. 30-day cure period for material defects.\n\n9. GOVERNING LAW: This Agreement shall be governed by French law. Disputes shall be resolved by the Commercial Court of Paris.\n\n10. FORCE MAJEURE: Neither party liable for delays due to events beyond reasonable control, provided affected party notifies within 5 business days."
            },
            {
              "id": "2",
              "name": "filename",
              "type": "string",
              "value": "sample-msa-techcorp.pdf"
            },
            {
              "id": "3",
              "name": "Contract Type",
              "type": "string",
              "value": "MSA"
            },
            {
              "id": "4",
              "name": "Counterparty Name",
              "type": "string",
              "value": "CloudVendor Solutions Inc."
            },
            {
              "id": "5",
              "name": "Your Name",
              "type": "string",
              "value": "Ahmed Test"
            },
            {
              "id": "6",
              "name": "Your Email",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "7",
              "name": "Department",
              "type": "string",
              "value": "Legal"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "06ff5e07-5631-4d13-998f-976be80288b5",
      "name": "Prepare Text",
      "type": "n8n-nodes-base.code",
      "position": [
        3920,
        2096
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\nconst raw = item.json?.contractText ?? item.json?.text ?? item.json?.data ?? '';\nconst pages = item.json?.pages;\nlet fullText = raw;\nif (Array.isArray(pages)) fullText = pages.map(p => p?.text ?? p).filter(Boolean).join('\\n\\n');\nif (!fullText) {\n  const found = Object.values(item.json).find(v => typeof v === 'string' && v.length > 100);\n  if (found) fullText = found;\n}\n\nconst config = $('Config').first()?.json ?? {};\nconst formData = item.json;\nconst contractText = String(fullText).slice(0, 12000);\nconst contractTextShort = contractText.substring(0, 4000);\n\nreturn { json: {\n  ...config,\n  contractText,\n  contractTextSafe: JSON.stringify(contractText).slice(1, -1),\n  contractTextShortSafe: JSON.stringify(contractTextShort).slice(1, -1),\n  charCount: String(fullText).length,\n  filename: formData['Contract Name'] ?? formData.filename ?? 'contract.pdf',\n  contract_type: formData['Contract Type'] ?? 'Other',\n  counterparty: formData['Counterparty Name'] ?? '',\n  submitter_name: formData['Your Name'] ?? '',\n  submitter_email: formData['Your Email'] ?? '',\n  department: formData['Department'] ?? ''\n} };"
      },
      "typeVersion": 2
    },
    {
      "id": "b6bc0ed1-4325-4d2a-8a81-ebf730dc9fac",
      "name": "Parse Classification",
      "type": "n8n-nodes-base.code",
      "position": [
        4384,
        2096
      ],
      "parameters": {
        "jsCode": "const resp = $input.first().json;\nconst raw = resp.choices?.[0]?.message?.content ?? '{}';\nlet classification = {};\ntry {\n  classification = JSON.parse(raw.replace(/```json?/g,'').replace(/```/g,'').trim());\n} catch(e) {\n  classification = { contract_type: 'Unknown', parties: [], jurisdiction: 'Unknown' };\n}\nconst prev = $('Prepare Text').first()?.json ?? {};\nreturn { json: { ...prev, classification } };"
      },
      "typeVersion": 2
    },
    {
      "id": "eebe98e1-641f-4930-ae9a-0005ea1b1ed2",
      "name": "Parse Risk Analysis",
      "type": "n8n-nodes-base.code",
      "position": [
        4816,
        2096
      ],
      "parameters": {
        "jsCode": "const resp = $input.first().json;\nconst raw = resp.choices?.[0]?.message?.content ?? '{}';\nlet analysis = {};\ntry {\n  analysis = JSON.parse(raw.replace(/```json?/g,'').replace(/```/g,'').trim());\n} catch(e) {\n  analysis = { overall_risk_score: 50, risk_level: 'MEDIUM', executive_summary: 'AI parse error \u2014 manual review recommended', key_clauses: [], top_risks: ['Parse error'] };\n}\n\nconst prev = $('Parse Classification').first()?.json ?? {};\nconst score = Math.min(100, Math.max(0, analysis.overall_risk_score ?? 50));\nlet riskLevel = analysis.risk_level ?? 'MEDIUM';\nif (score >= 85) riskLevel = 'CRITICAL';\nelse if (score >= 70) riskLevel = 'HIGH';\nelse if (score >= 40) riskLevel = 'MEDIUM';\nelse riskLevel = 'LOW';\n\nreturn { json: {\n  ...prev,\n  ...analysis,\n  overall_risk_score: score,\n  risk_level: riskLevel,\n  key_clauses: analysis.key_clauses ?? [],\n  top_risks: analysis.top_risks ?? [],\n  missing_clauses: analysis.missing_clauses ?? [],\n  obligations: analysis.obligations ?? [],\n  negotiation_points: analysis.negotiation_points ?? [],\n  compliance_flags: analysis.compliance_flags ?? [],\n  raw_text_preview: (prev.contractText || '').slice(0, 500),\n  status: score >= (prev.RISK_THRESHOLD ?? 70) ? 'flagged' : 'analyzed'\n} };"
      },
      "typeVersion": 2
    },
    {
      "id": "db2f0f88-2472-4d28-97e2-a6ab518c7996",
      "name": "Build Report",
      "type": "n8n-nodes-base.code",
      "position": [
        5040,
        2096
      ],
      "parameters": {
        "jsCode": "const d = $input.first().json;\nconst score = d.overall_risk_score;\nconst level = d.risk_level;\nconst clauses = (d.key_clauses || []).slice(0, 5);\nconst topRisks = (d.top_risks || []).slice(0, 5);\nconst missing = (d.missing_clauses || []).slice(0, 3);\nconst negotiations = (d.negotiation_points || []).slice(0, 3);\nconst compliance = (d.compliance_flags || []).slice(0, 3);\n\nconst emoji = level === 'CRITICAL' ? '\ud83d\udd34' : level === 'HIGH' ? '\ud83d\udfe0' : level === 'MEDIUM' ? '\ud83d\udfe1' : '\ud83d\udfe2';\nconst color = level === 'CRITICAL' ? '#dc2626' : level === 'HIGH' ? '#ea580c' : level === 'MEDIUM' ? '#ca8a04' : '#16a34a';\n\nconst slackBlocks = [\n  { type: 'header', text: { type: 'plain_text', text: `${emoji} Contract Analysis: ${d.filename}` } },\n  { type: 'section', text: { type: 'mrkdwn', text: `*Risk Score:* ${score}/100 \u2014 *${level}*\\n*Type:* ${d.classification?.contract_type ?? d.contract_type ?? 'N/A'} | *Jurisdiction:* ${d.classification?.jurisdiction ?? 'N/A'}\\n*Parties:* ${(d.classification?.parties ?? []).map(p => `${p.name} (${p.role})`).join(', ') || 'N/A'}\\n*Counterparty:* ${d.counterparty || 'N/A'} | *Submitted by:* ${d.submitter_name || 'N/A'} (${d.department || 'N/A'})` } },\n  { type: 'divider' },\n  { type: 'section', text: { type: 'mrkdwn', text: `*Executive Summary*\\n${d.executive_summary || 'N/A'}` } }\n];\n\nif (topRisks.length > 0) {\n  slackBlocks.push({ type: 'section', text: { type: 'mrkdwn', text: `*Top Risks*\\n${topRisks.map((r, i) => `${i+1}. ${r}`).join('\\n')}` } });\n}\nif (clauses.length > 0) {\n  slackBlocks.push({ type: 'section', text: { type: 'mrkdwn', text: `*Key Clauses*\\n${clauses.map(c => `\u2022 *${c.clause_type}* [${(c.risk_level || '').toUpperCase()}]: ${c.risk_explanation || c.excerpt || ''}`).join('\\n')}` } });\n}\nif (missing.length > 0) {\n  slackBlocks.push({ type: 'section', text: { type: 'mrkdwn', text: `*Missing Clauses*\\n${missing.map(m => `\u26a0\ufe0f ${m}`).join('\\n')}` } });\n}\nif (negotiations.length > 0) {\n  slackBlocks.push({ type: 'section', text: { type: 'mrkdwn', text: `*Negotiation Points*\\n${negotiations.map(n => `\ud83d\udca1 ${n}`).join('\\n')}` } });\n}\n\nslackBlocks.push({ type: 'context', elements: [{ type: 'mrkdwn', text: `Analyzed at ${new Date().toISOString()} | Contract Intelligence Pro` }] });\n\nconst clauseRows = clauses.map(c => {\n  const bg = c.risk_level === 'critical' ? '#fecaca' : c.risk_level === 'high' ? '#fed7aa' : c.risk_level === 'medium' ? '#fef08a' : '#bbf7d0';\n  return `<tr style=\"background:${bg}\"><td style=\"padding:8px;border:1px solid #e5e7eb\">${c.clause_type}</td><td style=\"padding:8px;border:1px solid #e5e7eb\">${(c.risk_level||'').toUpperCase()}</td><td style=\"padding:8px;border:1px solid #e5e7eb\">${c.risk_explanation || ''}</td><td style=\"padding:8px;border:1px solid #e5e7eb\">${c.recommendation || ''}</td></tr>`;\n}).join('');\n\nconst emailHtml = `\n<div style=\"font-family:Arial,sans-serif;max-width:700px;margin:0 auto\">\n  <div style=\"background:${color};color:white;padding:20px;border-radius:8px 8px 0 0\">\n    <h1 style=\"margin:0;font-size:22px\">${emoji} Contract Analysis Report</h1>\n    <p style=\"margin:8px 0 0;font-size:16px\">${d.filename} \u2014 Risk Score: ${score}/100 (${level})</p>\n  </div>\n  <div style=\"padding:20px;background:#f9fafb;border:1px solid #e5e7eb\">\n    <table style=\"width:100%;border-collapse:collapse;margin-bottom:16px\">\n      <tr><td style=\"padding:6px 12px;font-weight:bold;width:140px\">Contract Type</td><td style=\"padding:6px 12px\">${d.classification?.contract_type ?? d.contract_type ?? 'N/A'}</td></tr>\n      <tr><td style=\"padding:6px 12px;font-weight:bold\">Counterparty</td><td style=\"padding:6px 12px\">${d.counterparty || 'N/A'}</td></tr>\n      <tr><td style=\"padding:6px 12px;font-weight:bold\">Jurisdiction</td><td style=\"padding:6px 12px\">${d.classification?.jurisdiction ?? 'N/A'}</td></tr>\n      <tr><td style=\"padding:6px 12px;font-weight:bold\">Submitted by</td><td style=\"padding:6px 12px\">${d.submitter_name || 'N/A'} (${d.department || 'N/A'})</td></tr>\n      <tr><td style=\"padding:6px 12px;font-weight:bold\">Parties</td><td style=\"padding:6px 12px\">${(d.classification?.parties ?? []).map(p => `${p.name} (${p.role})`).join(', ') || 'N/A'}</td></tr>\n    </table>\n    <h2 style=\"color:#1f2937;font-size:16px;margin:16px 0 8px\">Executive Summary</h2>\n    <p style=\"color:#4b5563;line-height:1.6\">${d.executive_summary || 'N/A'}</p>\n    ${topRisks.length > 0 ? `<h2 style=\"color:#1f2937;font-size:16px;margin:16px 0 8px\">Top Risks</h2><ul style=\"color:#4b5563\">${topRisks.map(r => `<li>${r}</li>`).join('')}</ul>` : ''}\n    ${clauses.length > 0 ? `<h2 style=\"color:#1f2937;font-size:16px;margin:16px 0 8px\">Key Clauses</h2><table style=\"width:100%;border-collapse:collapse\"><tr style=\"background:#f3f4f6\"><th style=\"padding:8px;border:1px solid #e5e7eb;text-align:left\">Clause</th><th style=\"padding:8px;border:1px solid #e5e7eb;text-align:left\">Risk</th><th style=\"padding:8px;border:1px solid #e5e7eb;text-align:left\">Explanation</th><th style=\"padding:8px;border:1px solid #e5e7eb;text-align:left\">Recommendation</th></tr>${clauseRows}</table>` : ''}\n    ${missing.length > 0 ? `<h2 style=\"color:#1f2937;font-size:16px;margin:16px 0 8px\">Missing Clauses</h2><ul style=\"color:#dc2626\">${missing.map(m => `<li>${m}</li>`).join('')}</ul>` : ''}\n    ${negotiations.length > 0 ? `<h2 style=\"color:#1f2937;font-size:16px;margin:16px 0 8px\">Negotiation Points</h2><ul style=\"color:#2563eb\">${negotiations.map(n => `<li>${n}</li>`).join('')}</ul>` : ''}\n    ${compliance.length > 0 ? `<h2 style=\"color:#1f2937;font-size:16px;margin:16px 0 8px\">Compliance Flags</h2><ul style=\"color:#9333ea\">${compliance.map(c => `<li>${c}</li>`).join('')}</ul>` : ''}\n  </div>\n  <div style=\"padding:12px 20px;background:#f3f4f6;border-radius:0 0 8px 8px;text-align:center;color:#9ca3af;font-size:12px\">\n    Contract Intelligence Pro \u2014 Analyzed ${new Date().toISOString().split('T')[0]}\n  </div>\n</div>`;\n\nreturn { json: { ...d, slackBlocks: JSON.stringify(slackBlocks), emailHtml, emailSubject: `${emoji} [${level}] Contract Analysis: ${d.filename} \u2014 Score ${score}/100` } };"
      },
      "typeVersion": 2
    },
    {
      "id": "7f7d0872-3bb8-4480-b748-96722b07ca77",
      "name": "Supabase Insert",
      "type": "n8n-nodes-base.supabase",
      "position": [
        5264,
        2096
      ],
      "parameters": {
        "tableId": "contract_analyses"
      },
      "typeVersion": 1
    },
    {
      "id": "607470fa-deb2-4e4d-9a14-80494cf17b61",
      "name": "High Risk?",
      "type": "n8n-nodes-base.if",
      "position": [
        5472,
        2096
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": true
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.overall_risk_score }}",
              "rightValue": "={{ $json.RISK_THRESHOLD ?? 70 }}"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "c745489a-3292-424d-9d66-c9d10dd9c8cd",
      "name": "Form Ending",
      "type": "n8n-nodes-base.form",
      "position": [
        6144,
        2096
      ],
      "parameters": {
        "options": {},
        "operation": "completion",
        "completionTitle": "={{ 'Analysis Complete \u2014 ' + ($json.risk_level ?? 'N/A') }}",
        "completionMessage": "={{ 'Contract: ' + ($json.filename ?? 'N/A') + '\\nRisk Score: ' + ($json.overall_risk_score ?? 'N/A') + '/100 (' + ($json.risk_level ?? 'N/A') + ')\\nType: ' + ($json.classification?.contract_type ?? $json.contract_type ?? 'N/A') + '\\n\\n' + ($json.executive_summary ?? '') + '\\n\\nFull report stored in Supabase.' + ($json.ENABLE_EMAIL ? ' Email report sent to ' + ($json.submitter_email || $json.ALERT_EMAIL || '') + '.' : '') }}"
      },
      "typeVersion": 2.4
    },
    {
      "id": "45f67023-11d0-4198-a0dd-4172751ce974",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        4944,
        2352
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a10dab0a-9abd-4fa9-9ea5-8cb4ab4811fd",
      "name": "Slack Admin \u2014 Error",
      "type": "n8n-nodes-base.slack",
      "onError": "continueRegularOutput",
      "position": [
        5184,
        2352
      ],
      "parameters": {
        "operation": "create"
      },
      "typeVersion": 2.4
    },
    {
      "id": "4fcde5ae-05c7-4c05-ab89-72169fbe7c5a",
      "name": "AI Pass 1 - Classify",
      "type": "n8n-nodes-base.httpRequest",
      "maxTries": 2,
      "position": [
        4160,
        2096
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"{{ $json.AI_MODEL ?? 'gpt-4o-mini' }}\",\n  \"response_format\": { \"type\": \"json_object\" },\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a legal document classifier. Extract metadata from the contract. Reply ONLY with valid JSON. Language: {{ $json.CONTRACT_LANG ?? 'en' }}.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Classify this contract and return JSON with EXACTLY these fields: { \\\"contract_type\\\": \\\"NDA|MSA|SOW|Employment|SaaS|Consulting|Partnership|Other\\\", \\\"parties\\\": [{\\\"name\\\": string, \\\"role\\\": \\\"client|vendor|employer|employee|partner|other\\\"}], \\\"effective_date\\\": \\\"ISO date or unknown\\\", \\\"expiration_date\\\": \\\"ISO date or unknown\\\", \\\"auto_renewal\\\": boolean, \\\"jurisdiction\\\": \\\"country/region\\\", \\\"governing_law\\\": string, \\\"language\\\": string, \\\"estimated_value\\\": \\\"amount or unknown\\\" }. Submitted as: {{ $json.contract_type }}. Counterparty: {{ $json.counterparty }}.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"{{ $json.contractTextShortSafe }}\"\n    }\n  ],\n  \"temperature\": 0.1,\n  \"max_tokens\": 500\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "97fb58ef-4cc7-4d7d-b0f1-80a915064880",
      "name": "AI Pass 2 - Deep Risk",
      "type": "n8n-nodes-base.httpRequest",
      "maxTries": 2,
      "position": [
        4592,
        2096
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"{{ $json.AI_MODEL ?? 'gpt-4o-mini' }}\",\n  \"response_format\": { \"type\": \"json_object\" },\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a senior legal risk analyst specializing in contract review. Perform a thorough clause-by-clause risk assessment. Reply ONLY with valid JSON. Language: {{ $json.CONTRACT_LANG ?? 'en' }}.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Perform a deep risk analysis of this {{ $json.classification.contract_type ?? 'contract' }} between {{ ($json.classification.parties ?? []).map(p => p.name).join(' and ') || 'the parties' }}. Jurisdiction: {{ $json.classification.jurisdiction ?? 'Unknown' }}.\\n\\nReturn JSON with EXACTLY these fields:\\n{\\n  \\\"key_clauses\\\": [{ \\\"clause_type\\\": string, \\\"excerpt\\\": \\\"verbatim quote max 100 chars\\\", \\\"risk_level\\\": \\\"critical|high|medium|low\\\", \\\"risk_explanation\\\": string, \\\"recommendation\\\": string, \\\"negotiation_leverage\\\": \\\"strong|moderate|weak\\\" }],\\n  \\\"overall_risk_score\\\": 0-100,\\n  \\\"risk_level\\\": \\\"CRITICAL|HIGH|MEDIUM|LOW\\\",\\n  \\\"executive_summary\\\": \\\"3-5 sentences\\\",\\n  \\\"top_risks\\\": [\\\"concise risk descriptions\\\"],\\n  \\\"missing_clauses\\\": [\\\"important clauses not found in contract\\\"],\\n  \\\"obligations\\\": [{ \\\"party\\\": string, \\\"obligation\\\": string, \\\"deadline\\\": \\\"string or none\\\" }],\\n  \\\"negotiation_points\\\": [\\\"specific actionable suggestions\\\"],\\n  \\\"compliance_flags\\\": [\\\"GDPR/regulatory concerns\\\"]\\n}\\n\\nFocus areas: indemnification, liability caps, termination penalties, IP ownership, data protection, warranty, non-compete, auto-renewal, governing law, force majeure.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"{{ $json.contractTextSafe }}\"\n    }\n  ],\n  \"temperature\": 0.15,\n  \"max_tokens\": 2500\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "3951bc85-a646-4284-93d8-0dd60965d3fd",
      "name": "Slack Alert - High Risk",
      "type": "n8n-nodes-base.slack",
      "position": [
        5696,
        1904
      ],
      "parameters": {
        "operation": "create"
      },
      "typeVersion": 2.4
    },
    {
      "id": "06fbd53b-aba8-4de0-b8ea-2df20c8a7a71",
      "name": "Gmail - Risk Report",
      "type": "n8n-nodes-base.gmail",
      "onError": "continueRegularOutput",
      "position": [
        5696,
        2096
      ],
      "parameters": {
        "sendTo": "={{ $json.ENABLE_EMAIL ? ($json.submitter_email || $json.ALERT_EMAIL || 'legal@company.com') : '' }}",
        "message": "={{ $json.emailHtml }}",
        "options": {},
        "subject": "={{ $json.emailSubject }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "971832e8-72b7-4794-bf2f-0e1dacf61ad8",
      "name": "Slack Summary - Low Risk",
      "type": "n8n-nodes-base.slack",
      "position": [
        5696,
        2304
      ],
      "parameters": {
        "operation": "create"
      },
      "typeVersion": 2.4
    }
  ],
  "connections": {
    "Config": {
      "main": [
        [
          {
            "node": "Check Duplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has File?": {
      "main": [
        [
          {
            "node": "Extract PDF Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sample Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "High Risk?": {
      "main": [
        [
          {
            "node": "Slack Alert - High Risk",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gmail - Risk Report",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack Summary - Low Risk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Test": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sample Data": {
      "main": [
        [
          {
            "node": "Prepare Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Report": {
      "main": [
        [
          {
            "node": "Supabase Insert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Text": {
      "main": [
        [
          {
            "node": "AI Pass 1 - Classify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Slack Admin \u2014 Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Already Exists?": {
      "main": [
        [
          {
            "node": "Already Analyzed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Has File?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Duplicate": {
      "main": [
        [
          {
            "node": "Already Exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase Insert": {
      "main": [
        [
          {
            "node": "High Risk?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Contract": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract PDF Text": {
      "main": [
        [
          {
            "node": "Prepare Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Risk Analysis": {
      "main": [
        [
          {
            "node": "Build Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Pass 1 - Classify": {
      "main": [
        [
          {
            "node": "Parse Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Classification": {
      "main": [
        [
          {
            "node": "AI Pass 2 - Deep Risk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Pass 2 - Deep Risk": {
      "main": [
        [
          {
            "node": "Parse Risk Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Alert - High Risk": {
      "main": [
        [
          {
            "node": "Form Ending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Summary - Low Risk": {
      "main": [
        [
          {
            "node": "Form Ending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}