This workflow corresponds to n8n.io template #13671 — we link there as the canonical source.
This workflow follows the Chainllm → Form Trigger 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 →
{
"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
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Legal teams, operations managers, and freelancers who review contracts regularly and want to catch risky clauses before signing. Ideal for small teams without dedicated legal counsel.
Source: https://n8n.io/workflows/13671/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Automate your lead intake, scoring, and outreach pipeline. This workflow collects leads from forms, enriches and scores them using Relevance AI, routes them by quality, and triggers the right follow-u
Sales teams and B2B marketers who spend hours researching leads manually. If you've looked at Clay but didn't want the $149/month price tag, this workflow does the same job as a one-time n8n template.
This workflow automates B2B lead research for Japanese companies. Submit a company name through a web form, and the workflow queries two free Japanese government APIs for corporate registry and busine
Transform your sales pipeline with this comprehensive AI-powered platform that automates lead capture, scoring, revenue prediction, and sales team coordination. Perfect for B2B teams processing 50+ le
This workflow automates invoice processing and cash flow prediction by combining Google Gemini AI with form-based invoice capture, fraud detection, and financial reporting.