{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "d5b2ad9a-1c1e-4956-a8ff-b4005fed52c7",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        -224
      ],
      "parameters": {
        "width": 368,
        "height": 704,
        "content": "## Contract Review Workflow\n\n### How it works\n1. Upload a PDF contract through the form\n2. The Code node extracts text from the uploaded PDF\n3. Google Gemini analyzes the contract for risky clauses, unfavorable terms, and missing protections\n4. Results are parsed and scored by risk level\n5. Every review is logged to Google Sheets for tracking\n6. High-risk contracts trigger an immediate Slack alert\n\n### Setup steps\n1. Set up Google Gemini API credentials (free tier works)\n2. Connect your Google Sheets account and create a sheet called \"Contract Reviews\"\n3. Connect your Slack workspace and pick a channel for alerts\n4. Share the form URL with your legal team\n\n### Customization\n- Edit the prompt in the \"Analyze Contract\" node to focus on specific clause types\n- Adjust the risk threshold in the \"Check Risk Level\" node (default: score > 7)\n- Add more columns to the Sheets node for extra metadata"
      },
      "typeVersion": 1
    },
    {
      "id": "a3a133d1-061c-44bc-94e6-77fbe5528d2f",
      "name": "Note - PDF Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 396,
        "height": 500,
        "content": "## PDF processing\nExtracts raw text from the uploaded PDF for AI analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "8e3153ed-2580-40f4-af48-a2e521293930",
      "name": "Note - AI Analysis",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 488,
        "height": 508,
        "content": "## AI risk analysis\nGemini reviews each clause and assigns a risk score from 1-10."
      },
      "typeVersion": 1
    },
    {
      "id": "65263858-a89b-41de-a6b1-5f90fd5b2b95",
      "name": "Note - Logging",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 620,
        "height": 500,
        "content": "## Logging and alerts\nAll reviews go to Sheets. High-risk contracts get a Slack notification."
      },
      "typeVersion": 1
    },
    {
      "id": "06fb1897-5f8d-4eab-bb53-684582da640a",
      "name": "Upload Contract Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        176,
        -16
      ],
      "parameters": {
        "path": "76d78a1e-7b97-47eb-94a4-48ee4ae35635",
        "options": {},
        "formTitle": "Contract Review Upload",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "Contract PDF",
              "requiredField": true,
              "acceptFileTypes": ".pdf"
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Contract Type",
              "fieldOptions": {
                "values": [
                  {
                    "option": "NDA"
                  },
                  {
                    "option": "Service Agreement"
                  },
                  {
                    "option": "Employment Contract"
                  },
                  {
                    "option": "Licensing Agreement"
                  },
                  {
                    "option": "Other"
                  }
                ]
              }
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Notes"
            }
          ]
        },
        "formDescription": "Upload a PDF contract for AI-powered risk analysis"
      },
      "typeVersion": 2.1
    },
    {
      "id": "4c908457-c16b-418b-bc34-2b228d21c7ff",
      "name": "Extract PDF Text",
      "type": "n8n-nodes-base.code",
      "position": [
        384,
        -16
      ],
      "parameters": {
        "jsCode": "// Extract text content from the uploaded PDF binary data\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const binaryData = item.binary;\n  let extractedText = '';\n  \n  if (binaryData && binaryData.data) {\n    // Get the base64 PDF content\n    const pdfBase64 = binaryData.data.data;\n    // Basic text extraction from PDF binary\n    const buffer = Buffer.from(pdfBase64, 'base64');\n    const content = buffer.toString('utf8');\n    \n    // Extract readable text between stream markers\n    const textMatches = content.match(/\\((.*?)\\)/g);\n    if (textMatches) {\n      extractedText = textMatches\n        .map(m => m.slice(1, -1))\n        .filter(t => t.length > 2)\n        .join(' ');\n    }\n    \n    if (!extractedText || extractedText.length < 50) {\n      extractedText = 'PDF text extraction returned minimal content. The AI will work with available data.';\n    }\n  }\n  \n  results.push({\n    json: {\n      contractText: extractedText,\n      contractType: item.json['Contract Type'] || 'Unknown',\n      notes: item.json['Notes'] || '',\n      uploadedAt: new Date().toISOString()\n    }\n  });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "f1487b81-b65b-4c10-841d-b32ea35cdfa0",
      "name": "Analyze Contract",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        576,
        -16
      ],
      "parameters": {
        "text": "You are a legal contract analyst. Review the following contract text and provide a risk assessment.\n\n**Contract Text:**\n{{ $json.contract_text }}\n\nAnalyze and return ONLY valid JSON (no markdown):\n{\n  \"overall_risk\": \"low/medium/high/critical\",\n  \"risk_score\": 0-100,\n  \"key_clauses\": [\n    {\n      \"clause\": \"Clause name or section\",\n      \"risk_level\": \"low/medium/high\",\n      \"issue\": \"What the concern is\",\n      \"recommendation\": \"Suggested action\"\n    }\n  ],\n  \"missing_clauses\": [\"List of important clauses that are missing\"],\n  \"summary\": \"2-3 sentence overall assessment\",\n  \"auto_renewal\": true/false,\n  \"termination_notice_days\": 0,\n  \"liability_cap\": \"description or null\",\n  \"non_compete\": true/false\n}\n\nFocus on: auto-renewal traps, liability limitations, termination terms, IP ownership, indemnification, payment terms, and non-compete clauses.",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "a6fa7536-c898-4900-9057-ad784af49ffc",
      "name": "Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        576,
        144
      ],
      "parameters": {
        "options": {
          "temperature": 0.2
        },
        "modelName": "models/gemini-1.5-flash"
      },
      "typeVersion": 1
    },
    {
      "id": "effde126-da6a-48b5-9e3f-997c63df27d4",
      "name": "Parse Risk Results",
      "type": "n8n-nodes-base.code",
      "position": [
        864,
        -16
      ],
      "parameters": {
        "jsCode": "// Parse the AI response and structure the risk analysis\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const aiResponse = item.json.text || item.json.response || '';\n  let analysis;\n  \n  try {\n    // Try to extract JSON from the response\n    const jsonMatch = aiResponse.match(/\\{[\\s\\S]*\\}/);\n    if (jsonMatch) {\n      analysis = JSON.parse(jsonMatch[0]);\n    } else {\n      analysis = {\n        overallRiskScore: 5,\n        summary: aiResponse.substring(0, 200),\n        riskyClause: [],\n        missingProtections: [],\n        favorabilityScore: 5\n      };\n    }\n  } catch (e) {\n    analysis = {\n      overallRiskScore: 5,\n      summary: 'Could not parse structured response. Raw: ' + aiResponse.substring(0, 300),\n      riskyClause: [],\n      missingProtections: [],\n      favorabilityScore: 5\n    };\n  }\n  \n  const highRiskClauses = (analysis.riskyClause || []).filter(c => c.severity === 'high');\n  \n  results.push({\n    json: {\n      ...analysis,\n      highRiskCount: highRiskClauses.length,\n      totalRisks: (analysis.riskyClause || []).length,\n      isHighRisk: analysis.overallRiskScore > 7,\n      reviewedAt: new Date().toISOString(),\n      contractType: items[0].json.contractType || 'Unknown'\n    }\n  });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "d51306c3-a9ce-4438-998c-799b5629c9b3",
      "name": "Log Review to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1088,
        -16
      ],
      "parameters": {
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "04fc4e89-b564-48df-8b7b-afc5eec9d2e3",
      "name": "Check Risk Level",
      "type": "n8n-nodes-base.if",
      "position": [
        1264,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "risk-check",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.overallRiskScore }}",
              "rightValue": 7
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "898cf735-8109-47ad-9a24-c3d0b6b99714",
      "name": "Send Risk Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        1472,
        -32
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *High-Risk Contract Detected*\n\n*Type:* {{ $json.contractType }}\n*Risk Score:* {{ $json.overallRiskScore }}/10\n*Favorability:* {{ $json.favorabilityScore }}/10\n\n*Summary:* {{ $json.summary }}\n\n*High-Risk Clauses:* {{ $json.highRiskCount }}\n*Total Issues:* {{ $json.totalRisks }}\n*Missing Protections:* {{ $json.missingProtections.join(', ') }}\n\n\u26a0\ufe0f Review this contract before signing.",
        "otherOptions": {}
      },
      "typeVersion": 2.2
    }
  ],
  "connections": {
    "Analyze Contract": {
      "main": [
        [
          {
            "node": "Parse Risk Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Risk Level": {
      "main": [
        [
          {
            "node": "Send Risk Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract PDF Text": {
      "main": [
        [
          {
            "node": "Analyze Contract",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Analyze Contract",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Parse Risk Results": {
      "main": [
        [
          {
            "node": "Log Review to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Review to Sheets": {
      "main": [
        [
          {
            "node": "Check Risk Level",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Contract Form": {
      "main": [
        [
          {
            "node": "Extract PDF Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}