법률 계약 데이터 추출 및 VLM Run, Google Workspace, Slack을 통해 알림 전송
이것은자동화 워크플로우로, 14개의 노드를 포함합니다.주로 Set, Code, Slack, Webhook, GoogleDrive 등의 노드를 사용하며. 법률 계약 데이터 추출하고 VLM Run, Google Workspace, Slack을 사용하여 알림을 보냅니다
- •Slack Bot Token 또는 Webhook URL
- •HTTP Webhook 엔드포인트(n8n이 자동으로 생성)
- •Google Drive API 인증 정보
- •Google Sheets API 인증 정보
사용된 노드 (14)
카테고리
{
"meta": {
"instanceId": "96d35e452e0d9a182973416b7532cfc5643239aaaa764a5bf74d52ca84f4a35c",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "bdfebade-893c-411c-80c6-0dace07f7dc9",
"name": "📁 입력 처리 문서화",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"color": 7,
"width": 400,
"height": 680,
"content": "## 📁 Input Processing\n\n**Monitors & downloads contract files from Google Drive.**\n\n**Process:**\n1. Watches designated Drive folder\n2. Auto-triggers on new uploads\n3. Downloads files for AI processing\n\n**Supported Formats:**\n- Images (JPG, PNG, WEBP)\n- PDF documents\n- Mobile camera uploads\n- Scanned contracts"
},
"typeVersion": 1
},
{
"id": "2fca40fa-ac4a-4515-a1e0-295cdcbfc595",
"name": "계약 업로드 모니터링",
"type": "n8n-nodes-base.googleDriveTrigger",
"notes": "Monitors Google Drive folder for new receipt uploads and triggers processing automatically.",
"position": [
48,
480
],
"parameters": {
"event": "fileCreated",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"triggerOn": "specificFolder",
"folderToWatch": {
"__rl": true,
"mode": "list",
"value": "1S6baavqJn98MjUlbB6KtmARCWuWEekIZ",
"cachedResultUrl": "https://drive.google.com/drive/folders/1S6baavqJn98MjUlbB6KtmARCWuWEekIZ",
"cachedResultName": "test_data"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "zYyIOFMdGz258avn",
"name": "Google Drive account 6"
}
},
"typeVersion": 1
},
{
"id": "95acb226-9205-4a21-9a2d-c9792bc81de4",
"name": "계약 파일 다운로드",
"type": "n8n-nodes-base.googleDrive",
"notes": "Downloads receipt files from Google Drive for AI processing.",
"position": [
256,
480
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.id }}"
},
"options": {
"binaryPropertyName": "data"
},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "zYyIOFMdGz258avn",
"name": "Google Drive account 6"
}
},
"typeVersion": 3
},
{
"id": "61fa2c83-72cd-44b0-a5cd-5fbaeef5dfb1",
"name": "🤖 AI 추출 문서화",
"type": "n8n-nodes-base.stickyNote",
"position": [
512,
0
],
"parameters": {
"width": 416,
"height": 680,
"content": "## 🤖 VLM Run Contract Extraction\n\n**Uses VLM Run node to extract structured data from contract images/PDFs.**\n\n**Extracts:**\n- Contract ID\n- Title\n- Parties\n- Effective Date\n- Termination Date\n\n**Features:**\n- Handles poor quality images\n- Various receipt formats\n- OCR text recognition"
},
"typeVersion": 1
},
{
"id": "84d28c8a-b33c-4bc3-9489-846c3b412d4a",
"name": "VLM Run ContractParser",
"type": "@vlm-run/n8n-nodes-vlmrun.vlmRun",
"position": [
560,
480
],
"parameters": {
"operation": "executeAgent",
"agentPrompt": "extract data from the invoice or contract, Extract the key details from this lease contract. I am expecting the output to be in JSON format, with tags, using parameters : contract_id, title, parties (with role), property_address, effective_date, termination_date, rent_amount, security_deposit, payment_terms, governing_law . All the parameters should get values from the pdf of data file from the download contract file. \nNormalize dates to YYYY-MM-DD and amounts as numbers with currency. \nIf a field is missing, return null.",
"agentCallbackUrl": "https://playground.attensys.ai/webhook/b905e71d-8ea5-4fc2-a773-b0f92e5398e4"
},
"credentials": {
"vlmRunApi": {
"id": "B7ZYM8AfBgjnOEOl",
"name": "VLM Run account 5"
}
},
"typeVersion": 1
},
{
"id": "17996d92-afea-484c-88d3-9b2b673b1f4c",
"name": "📊 저장소 문서화",
"type": "n8n-nodes-base.stickyNote",
"position": [
1056,
0
],
"parameters": {
"color": 7,
"width": 420,
"height": 680,
"content": "## 📊 Data Storage\n\n**Structures and stores extracted data in Google Sheets.**\n\n**Features:**\n- Clean, organized format\n- Centralized expense database\n- Auto-appends new entries\n- Analysis-ready data\n\n**Data Fields:**\n- ID\n- Title\n- Parties\n- Effective and Termination Date\n\n**Benefits:**\n- Real-time tracking\n- Easy exports\n- Mobile access"
},
"typeVersion": 1
},
{
"id": "70c2c9c5-d950-495f-9e91-5a0b402ef739",
"name": "비용 데이터베이스에 저장",
"type": "n8n-nodes-base.googleSheets",
"notes": "Automatically saves extracted receipt data to Google Sheets for expense tracking.",
"position": [
1344,
480
],
"parameters": {
"columns": {
"value": {
"Title": "={{$node[\"Format Contract Data\"].json[\"Title\"]}}",
"Parties": "={{$node[\"Format Contract Data\"].json[\"Parties\"]}}",
"Contract ID": "={{$node[\"Format Contract Data\"].json[\"Contract ID\"]}}",
"Effective Date": "={{$node[\"Format Contract Data\"].json[\"Effective Date\"]}}",
"Termination Date": "={{$node[\"Format Contract Data\"].json[\"Termination Date\"]}}"
},
"schema": [
{
"id": "Contract ID",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Contract ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Title",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Parties",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Parties",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Effective Date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Effective Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Termination Date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Termination Date",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Customer"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/11_VjMdhv_JN2eSRZiw_t0dIN-yShkn2jlCDwiG8eb14/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1lg0aJKvd7E2pbhumHNjcgxUfEQKvlBs9h1zZbhSeqas",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1lg0aJKvd7E2pbhumHNjcgxUfEQKvlBs9h1zZbhSeqas/edit?usp=drivesdk",
"cachedResultName": "test"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "sdLQgQJjowDNfXMU",
"name": "Google Sheets account 7"
}
},
"typeVersion": 4.6
},
{
"id": "f752ffb1-d4eb-4d69-919d-2f4d72fb753e",
"name": "계약 데이터 형식화",
"type": "n8n-nodes-base.set",
"notes": "Transforms AI-extracted receipt data into clean, structured format for spreadsheet storage.",
"position": [
1136,
480
],
"parameters": {
"values": {
"string": [
{
"name": "Contract ID",
"value": "={{ $json.body.response.contract_id }}"
},
{
"name": "Title",
"value": "={{ $json.body.response.title }}"
},
{
"name": "Parties",
"value": "={{ $json.body.parties && $json.body.parties.length ? $json.body.parties.map(p => p.name + \" (\"+ p.role + \")\").join(\"; \") : \"\" }}"
},
{
"name": "Effective Date",
"value": "={{ $json.body.response.effective_date }}"
},
{
"name": "Termination Date",
"value": "={{ $json.body.response.termination_date }}"
}
]
},
"options": {},
"keepOnlySet": true
},
"typeVersion": 1
},
{
"id": "d04b627c-1b80-4692-91a3-2df61bec0b4d",
"name": "메시지 전송",
"type": "n8n-nodes-base.slack",
"position": [
1712,
512
],
"webhookId": "6b8dcfb9-51a9-418b-8469-4bf5b5894f2a",
"parameters": {
"text": "=*New Contract Processed* 📄\n• Contract ID: {{$node[\"Format Contract Data\"].json[\"Contract ID\"] || \"N/A\"}}\n• Title: {{$node[\"Format Contract Data\"].json[\"Title\"] || \"N/A\"}}\n• Parties: {{$node[\"Format Contract Data\"].json[\"Parties\"] || \"N/A\"}}\n• Effective: {{$node[\"Format Contract Data\"].json[\"Effective Date\"] || \"N/A\"}}\n• Termination: {{$node[\"Format Contract Data\"].json[\"Termination Date\"] || \"N/A\"}}\n🔗 {{$node[\"Format Contract Data\"].json[\"Drive Link\"] || $node[\"Save to Expense Database\"].json[\"driveLink\"] || \"No drive link available\"}}\n",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C081Z0KL546",
"cachedResultName": "test"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"id": "M00QrTNTmnr6yiTS",
"name": "Slack account 11"
}
},
"typeVersion": 2.3
},
{
"id": "955768a3-5fac-4296-a5a3-d307a98b471c",
"name": "캘린더 이벤트 준비",
"type": "n8n-nodes-base.code",
"position": [
1616,
336
],
"parameters": {
"jsCode": "// n8n Function node code\n// Reads from Format Contract Data node and returns 1..3 items, each has .json with summary, description, start, end (YYYY-MM-DD)\n\nconst fmt = $node[\"Format Contract Data\"].json;\nconst title = fmt[\"Title\"] || \"Untitled Contract\";\nconst cid = fmt[\"Contract ID\"] || \"\";\nconst parties = fmt[\"Parties\"] || \"\";\nconst drive = fmt[\"Drive Link\"] || \"\";\n\nfunction isoDate(d) {\n // return YYYY-MM-DD\n return d.toISOString().split('T')[0];\n}\n\nfunction makeAllDayDates(dateStr) {\n // Accepts YYYY-MM-DD (or other parseable), returns start and end for all-day event.\n if(!dateStr) return null;\n const d = new Date(dateStr + \"T00:00:00\");\n const start = isoDate(d);\n const endD = new Date(d);\n endD.setDate(endD.getDate() + 1); // exclusive end for all-day events\n const end = isoDate(endD);\n return { start, end };\n}\n\nconst items = [];\n\n// Effective Date event\nconst eff = makeAllDayDates(fmt[\"Effective Date\"]);\nif (eff) {\n items.push({\n json: {\n summary: `${title} — Effective Date`,\n description: `Contract ID: ${cid}\\nParties: ${parties}\\nDrive: ${drive}`,\n start: eff.start,\n end: eff.end,\n allDay: true,\n metaType: \"effective\"\n }\n });\n}\n\n// Termination Date event\nconst term = makeAllDayDates(fmt[\"Termination Date\"]);\nif (term) {\n items.push({\n json: {\n summary: `${title} — Termination Date`,\n description: `Contract ID: ${cid}\\nParties: ${parties}\\nDrive: ${drive}`,\n start: term.start,\n end: term.end,\n allDay: true,\n metaType: \"termination\"\n }\n });\n\n // Renewal review reminder 60 days before termination\n const termDate = new Date(fmt[\"Termination Date\"] + \"T00:00:00\");\n const reminderDate = new Date(termDate);\n reminderDate.setDate(reminderDate.getDate() - 60); // change 60 to your preferred days\n const remStart = isoDate(reminderDate);\n const remEndD = new Date(reminderDate);\n remEndD.setDate(remEndD.getDate() + 1);\n const remEnd = isoDate(remEndD);\n\n items.push({\n json: {\n summary: `${title} — Renewal Review (Reminder)`,\n description: `Contract ID: ${cid}\\nTermination Date: ${fmt[\"Termination Date\"]}\\nAction: Review & decide renewal.`,\n start: remStart,\n end: remEnd,\n allDay: true,\n metaType: \"renewal_reminder\"\n }\n });\n}\n\nreturn items;\n"
},
"typeVersion": 2
},
{
"id": "f3301d1a-5773-4598-ba4e-71019a4966bd",
"name": "이벤트 생성",
"type": "n8n-nodes-base.googleCalendar",
"position": [
1808,
336
],
"parameters": {
"end": "={{$json[\"end\"]}}",
"start": "={{$json[\"start\"]}}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "sayonaraistata@gmail.com",
"cachedResultName": "sayonaraistata@gmail.com"
},
"additionalFields": {
"allday": "yes",
"summary": "={{$json[\"summary\"]}}",
"description": "={{$json[\"description\"]}}"
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "9TLut5ZDt3hcaQEo",
"name": "Google Calendar account 3"
}
},
"typeVersion": 1.3
},
{
"id": "dd656271-d2a3-4f8a-9df8-1da0596b2734",
"name": "📊 저장소 문서화1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1552,
0
],
"parameters": {
"color": 7,
"width": 420,
"height": 680,
"content": "## 🔔 Notifications Added:\n\n1. **Slack** → Posts contract details & AI-generated summary to #all-n8n-test for team visibility.\n\n2. **Google Calendar** → Auto-creates all-day events:\n • Effective Date\n • Termination Date\n • Renewal Reminder (30 days before)\n → Helps track key contract milestones.\n"
},
"typeVersion": 1
},
{
"id": "99fdd61d-c6a9-4e74-989a-e56b47caf2e9",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
368,
704
],
"parameters": {
"color": 5,
"width": 704,
"height": 144,
"content": "## 📝 Set the Callback URL\n\nMake sure to paste the **Production URL** of your n8n Webhook node into the **Callback URL** field in VLM Run. This ensures that the extracted image links are securely and reliably sent back to your live workflow. Do **not** use localhost or development URLs, they won’t be reachable by VLM Run’s servers."
},
"typeVersion": 1
},
{
"id": "6d26e945-a724-4e3b-a8ff-c91dd8500f5f",
"name": "계약 수신",
"type": "n8n-nodes-base.webhook",
"position": [
784,
480
],
"webhookId": "b905e71d-8ea5-4fc2-a773-b0f92e5398e4",
"parameters": {
"path": "b905e71d-8ea5-4fc2-a773-b0f92e5398e4",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2.1
}
],
"pinData": {},
"connections": {
"6d26e945-a724-4e3b-a8ff-c91dd8500f5f": {
"main": [
[
{
"node": "f752ffb1-d4eb-4d69-919d-2f4d72fb753e",
"type": "main",
"index": 0
}
]
]
},
"f752ffb1-d4eb-4d69-919d-2f4d72fb753e": {
"main": [
[
{
"node": "70c2c9c5-d950-495f-9e91-5a0b402ef739",
"type": "main",
"index": 0
}
]
]
},
"95acb226-9205-4a21-9a2d-c9792bc81de4": {
"main": [
[
{
"node": "84d28c8a-b33c-4bc3-9489-846c3b412d4a",
"type": "main",
"index": 0
}
]
]
},
"955768a3-5fac-4296-a5a3-d307a98b471c": {
"main": [
[
{
"node": "f3301d1a-5773-4598-ba4e-71019a4966bd",
"type": "main",
"index": 0
}
]
]
},
"2fca40fa-ac4a-4515-a1e0-295cdcbfc595": {
"main": [
[
{
"node": "95acb226-9205-4a21-9a2d-c9792bc81de4",
"type": "main",
"index": 0
}
]
]
},
"70c2c9c5-d950-495f-9e91-5a0b402ef739": {
"main": [
[
{
"node": "d04b627c-1b80-4692-91a3-2df61bec0b4d",
"type": "main",
"index": 0
},
{
"node": "955768a3-5fac-4296-a5a3-d307a98b471c",
"type": "main",
"index": 0
}
]
]
}
}
}이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
Shahrear
@shahrearI’m Shahrear, a Software Engineer with over 5 years of experience in full-stack development and workflow automation. I specialize in building intelligent automations using n8n, helping teams streamline operations and boost productivity. I’m also an expert in developing custom n8n nodes, with published work on npm - including the @vlm-run/n8n-nodes-vlmrun package. Linkedin - https://www.linkedin.com/in/shahrear-amin/ Email - shahrearbinamin33@gmail.com
이 워크플로우 공유