This workflow corresponds to n8n.io template #17806 — we link there as the canonical source.
This workflow follows the HTTP Request → OpenAI 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 →
{
"id": "jDsblvHxwDpJIQgk",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "IDP - Secure Invoice Processing",
"tags": [],
"nodes": [
{
"id": "eb433f1d-d606-4f7c-a0a2-595d2a9c81c1",
"name": "Note - Secure Intake",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-192
],
"parameters": {
"color": 5,
"width": 760,
"height": 596,
"content": "## 1. Secure Intake & Document Validation\n\nReceives invoices through the webhook and performs initial validation before any downstream processing.\n\n### Responsibilities\n\u2022 Accept document upload\n\u2022 Validate supported file types (PDF, PNG, JPG)\n\u2022 Validate maximum file size\n\u2022 Verify malware scan status\n\u2022 Generate Correlation ID\n\u2022 Preserve binary document\n\u2022 Create standardized metadata\n\n### Expected Output\n\u2714 Validated document\n\u2714 Metadata\n\u2714 Correlation ID\n\u2714 Source information\n\nInvalid documents are routed directly to the exception queue."
},
"typeVersion": 1
},
{
"id": "475d7ba6-8410-4fd3-b3f0-6c894388617e",
"name": "Ingest Document (Webhook)",
"type": "n8n-nodes-base.webhook",
"position": [
-656,
576
],
"parameters": {
"path": "idp-ingest",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2
},
{
"id": "358c20a3-c0c6-4dfc-9435-a6cc8b26b022",
"name": "Validate File & Create Metadata",
"type": "n8n-nodes-base.code",
"position": [
-240,
576
],
"parameters": {
"jsCode": "const input = $input.item;\n\n// Preserve the binary file\nconst binaryKey = Object.keys(input.binary || {})[0];\nconst binary = binaryKey ? input.binary[binaryKey] : null;\n\nconst supplied = input.json.body ?? input.json;\n\nconst allowedMimes = [\n 'application/pdf',\n 'image/jpeg',\n 'image/png'\n];\n\nconst maxBytes = 10 * 1024 * 1024;\n\nconst mime = binary?.mimeType ?? supplied.mime_type ?? '';\nconst fileName = binary?.fileName ?? supplied.file_name ?? 'unknown';\n\n// n8n stores the numeric size in bytes\nconst size = Number(\n binary?.bytes ??\n binary?.size ??\n supplied.file_size ??\n 0\n);\n\nconst violations = [];\n\nif (!allowedMimes.includes(mime)) {\n violations.push('INT-01: Unsupported or missing file type');\n}\n\nif (size > maxBytes) {\n violations.push('INT-02: File exceeds 10 MB limit');\n}\n\nif (\n supplied.malware_scan_status &&\n supplied.malware_scan_status !== 'clean'\n) {\n violations.push('INT-03: Malware scan did not pass');\n}\n\nconst correlation_id = supplied.correlation_id || $execution.id;\nconst source_id = supplied.source_id || `SRC-${correlation_id}-${fileName}`;\n\nreturn [{\n json: {\n correlation_id,\n source_id,\n\n document_metadata: {\n file_name: fileName,\n mime_type: mime,\n file_size: size,\n received_at: new Date().toISOString(),\n source_sha256: supplied.source_sha256 || null\n },\n\n intake_validation: {\n passed: violations.length === 0,\n violations\n },\n\n test_mode: supplied.test_mode === true,\n force_duplicate: supplied.force_duplicate === true\n },\n\n // \u2b50 Preserve the binary for downstream nodes\n binary: input.binary\n}];"
},
"typeVersion": 2
},
{
"id": "07ebeda9-63d6-43e0-87e8-78d8d2d4170c",
"name": "Route Intake Status",
"type": "n8n-nodes-base.switch",
"position": [
-64,
576
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Valid intake",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "7ba9421e-7677-4d66-8556-415da2a01cbc",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.intake_validation.passed }}",
"rightValue": true
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"typeVersion": 3
},
{
"id": "00495324-23ae-485d-8d19-1fd44d80d415",
"name": "Note - Evidence & Idempotency",
"type": "n8n-nodes-base.stickyNote",
"position": [
96,
432
],
"parameters": {
"color": 4,
"width": 438,
"height": 338,
"content": "\ud83d\udfe9 2. Evidence Store & Duplicate Detection"
},
"typeVersion": 1
},
{
"id": "b7b2233b-e188-43dd-b6c7-895e4de91b70",
"name": "Duplicate Check (Replace With DB)",
"type": "n8n-nodes-base.code",
"position": [
176,
560
],
"parameters": {
"jsCode": "// DEMO ONLY: replace with an atomic database insert/lookup on source_sha256.\n// Production should use a UNIQUE(source_sha256) constraint.\n\nconst input = $input.item;\n\nconst item = input.json;\n\n// Simulate duplicate detection\nconst duplicate = item.force_duplicate === true;\n\nreturn [{\n json: {\n ...item,\n duplicate_check: {\n is_duplicate: duplicate,\n key: item.document_metadata.source_sha256 || item.source_id,\n checked_at: new Date().toISOString()\n }\n },\n\n // Preserve binary for downstream nodes\n binary: input.binary\n}];"
},
"typeVersion": 2
},
{
"id": "68f5b858-e07b-4c14-92bc-b659f7246b37",
"name": "Route Duplicate Status",
"type": "n8n-nodes-base.switch",
"position": [
352,
560
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "New document",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "fb9b0896-adba-4dff-9ae5-d2f4e7e402da",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.duplicate_check.is_duplicate }}",
"rightValue": false
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"typeVersion": 3
},
{
"id": "8a8a00fa-e97e-4a4e-9358-7d4f95b55e2b",
"name": "Note - AI Control",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
432
],
"parameters": {
"color": 3,
"width": 620,
"height": 340,
"content": "\ud83d\udfe5 3. OCR & AI Invoice Extraction"
},
"typeVersion": 1
},
{
"id": "442df7ac-98d6-4749-9dad-a776bfa34f7d",
"name": "Validation Policy Engine",
"type": "n8n-nodes-base.code",
"position": [
1248,
544
],
"parameters": {
"jsCode": "const item = $input.item.json;\n\nconst violations = [];\n\n// ---------- Vendor Validation ----------\nif (!item.vendor?.name) {\n violations.push(\"VAL-01: Vendor name is missing\");\n}\n\nif (!item.vendor?.id) {\n violations.push(\"VAL-02: Vendor ID not found in Vendor Master\");\n}\n\n// ---------- Invoice Validation ----------\nif (!item.invoice?.number) {\n violations.push(\"VAL-03: Invoice number is missing\");\n}\n\nif (!item.invoice?.issue_date) {\n violations.push(\"VAL-04: Invoice date is missing\");\n}\n\nif (!item.invoice?.due_date) {\n violations.push(\"VAL-05: Due date is missing\");\n}\n\nif (!item.invoice?.currency) {\n violations.push(\"VAL-06: Currency is missing\");\n}\n\n// ---------- Amount Validation ----------\nif (item.amounts?.subtotal == null || item.amounts.subtotal < 0) {\n violations.push(\"VAL-07: Invalid subtotal\");\n}\n\nif (item.amounts?.tax == null || item.amounts.tax < 0) {\n violations.push(\"VAL-08: Invalid tax amount\");\n}\n\nif (item.amounts?.total == null || item.amounts.total <= 0) {\n violations.push(\"VAL-09: Invalid total amount\");\n}\n\n// ---------- Line Items ----------\nif (!Array.isArray(item.line_items) || item.line_items.length === 0) {\n violations.push(\"VAL-10: No line items found\");\n}\n\n// ---------- Confidence ----------\nconst confidence = item.confidence || {};\n\nif (\n confidence.vendor_name != null &&\n confidence.vendor_name < 0.80\n) {\n violations.push(\"VAL-11: Low confidence on Vendor Name\");\n}\n\nif (\n confidence.invoice_number != null &&\n confidence.invoice_number < 0.80\n) {\n violations.push(\"VAL-12: Low confidence on Invoice Number\");\n}\n\nif (\n confidence.total_amount != null &&\n confidence.total_amount < 0.80\n) {\n violations.push(\"VAL-13: Low confidence on Total Amount\");\n}\n\nreturn [{\n json: {\n ...item,\n\n validation_policy: {\n version: \"2.0\",\n passed: violations.length === 0,\n violations,\n evaluated_at: new Date().toISOString()\n }\n },\n\n binary: $input.item.binary\n}];"
},
"typeVersion": 2
},
{
"id": "9aaea83a-bec5-42a0-9c69-96190cd36895",
"name": "Route by Policy Status",
"type": "n8n-nodes-base.switch",
"position": [
1440,
544
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Auto-approved",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "6f44ba6b-9b62-4504-ba64-7bf338e3350d",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.validation_policy.passed }}",
"rightValue": true
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"typeVersion": 3
},
{
"id": "1737a3c3-cb72-4259-8b03-628b78d8aa40",
"name": "Note - Human Review",
"type": "n8n-nodes-base.stickyNote",
"position": [
1744,
432
],
"parameters": {
"color": 6,
"width": 1124,
"height": 348,
"content": "\ud83d\udfea 5. Human Review & Exception Handling"
},
"typeVersion": 1
},
{
"id": "fd7cb31d-a1f0-4474-8793-2627c1fb6227",
"name": "Create Exception Record",
"type": "n8n-nodes-base.code",
"position": [
1792,
592
],
"parameters": {
"jsCode": "const item = $input.item.json;\nreturn [{ json: { ...item, exception: { queue: 'IDP_MANUAL_REVIEW', status: 'PENDING_REVIEW', reasons: item.validation_policy?.violations || item.intake_validation?.violations || ['Duplicate document'], raised_at: new Date().toISOString() } } }];"
},
"typeVersion": 2
},
{
"id": "35321ac2-de9d-4d5d-831c-b6415097a697",
"name": "Wait for Human Review",
"type": "n8n-nodes-base.wait",
"position": [
2240,
592
],
"parameters": {
"resume": "webhook",
"options": {}
},
"typeVersion": 1.1
},
{
"id": "eb62fe36-44e8-4d16-a695-b7381a791ad2",
"name": "Validate Review Decision",
"type": "n8n-nodes-base.code",
"position": [
2496,
592
],
"parameters": {
"jsCode": "const item = $input.item.json;\nconst review = item.review_decision ?? item.body?.review_decision;\nconst reviewer = item.reviewer_id ?? item.body?.reviewer_id;\nconst decision = review === 'approved' && reviewer ? 'approved' : 'rejected';\nreturn [{ json: { ...item, human_review: { decision, reviewer_id: reviewer || 'UNVERIFIED', correction_reason: item.correction_reason ?? item.body?.correction_reason ?? null, decided_at: new Date().toISOString() } } }];"
},
"typeVersion": 2
},
{
"id": "1f5463ec-ef39-4dd1-b4d1-a11cb6cc9208",
"name": "Route Review Decision",
"type": "n8n-nodes-base.switch",
"position": [
2704,
592
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Approved",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.human_review.decision }}",
"rightValue": "approved"
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"typeVersion": 3
},
{
"id": "0ed83c22-360d-4ecb-b286-13ef489ad786",
"name": "Note - ERP & Recovery",
"type": "n8n-nodes-base.stickyNote",
"position": [
2880,
-192
],
"parameters": {
"color": 2,
"width": 692,
"height": 595,
"content": "## 6. ERP Posting & Recovery\n\nPosts approved invoices to ERP using idempotent transactions.\n\n### Integration Controls\n\u2022 Idempotency-Key\n\u2022 Approved Credentials\n\u2022 Correlation ID\n\u2022 Retry Strategy\n\u2022 Timeout Handling\n\nProduction Recommendations\n\u2022 Retry only 429/5xx\n\u2022 Exponential Backoff\n\u2022 Dead Letter Queue\n\u2022 Reconciliation before retry"
},
"typeVersion": 1
},
{
"id": "6e8296ff-e6d4-4037-85f4-a32d348cc067",
"name": "Idempotent ERP Post (Configure Credential)",
"type": "n8n-nodes-base.httpRequest",
"position": [
2992,
464
],
"parameters": {
"url": "https://REPLACE_WITH_ERP_ENDPOINT",
"options": {
"timeout": 15000
},
"jsonBody": "={{ { erp_system: 'SAP_PROD', idempotency_key: $json.document_id || $json.source_id, vendor_id: $json.vendor_id, invoice_number: $json.invoice_number, total_amount: $json.total_amount, currency: $json.currency, correlation_id: $json.correlation_id } }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Idempotency-Key",
"value": "={{ $json.document_id || $json.source_id }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "383068fa-e646-48bd-b23c-61f096502731",
"name": "Generate Sanitized Audit Log",
"type": "n8n-nodes-base.code",
"position": [
3216,
464
],
"parameters": {
"jsCode": "const item = $input.item.json;\nreturn [{ json: { audit_version: '1.0', correlation_id: item.correlation_id || $execution.id, source_id: item.source_id, document_id: item.document_id || item.source_id, decision: item.human_review?.decision || (item.validation_policy?.passed ? 'auto_approved' : 'exception'), validation: item.validation_policy || item.intake_validation, duplicate_check: item.duplicate_check, ai_provenance: item.ai_provenance, erp_integration_execution: { target_system: 'SAP_ERP_PROD', posted_at: new Date().toISOString(), status: 'SUCCESS', idempotency_key: item.document_id || item.source_id } } }];"
},
"typeVersion": 2
},
{
"id": "ed4320fb-ca87-47ef-abe9-d6df11549a38",
"name": "Record Rejection / Duplicate",
"type": "n8n-nodes-base.code",
"position": [
2992,
624
],
"parameters": {
"jsCode": "const item = $input.item.json;\nreturn [{ json: { audit_version: '1.0', correlation_id: item.correlation_id || $execution.id, source_id: item.source_id || item.document_id, status: 'CLOSED_NO_POST', reason: item.human_review?.decision === 'rejected' ? 'REVIEW_REJECTED' : (item.exception?.reasons || item.intake_validation?.violations || ['DUPLICATE']).join('; '), closed_at: new Date().toISOString() } }];"
},
"typeVersion": 2
},
{
"id": "f2206618-4a2e-4f99-9208-d0218b15dafb",
"name": "Note - Deployment Checklist",
"type": "n8n-nodes-base.stickyNote",
"position": [
-672,
816
],
"parameters": {
"color": "#DCC1C1",
"width": 674,
"height": 389,
"content": "## Mandatory Deployment Checklist\n\n\u25a1 Replace demo nodes with production OCR/AI services\n\u25a1 Configure ERP credentials\n\u25a1 Configure notification credentials\n\u25a1 Secure webhook authentication\n\u25a1 Enable retry workflow\n\u25a1 Enable monitoring\n\u25a1 Test duplicate scenarios\n\u25a1 Test low confidence routing\n\u25a1 Test ERP outage recovery\n\u25a1 Verify audit logs"
},
"typeVersion": 1
},
{
"id": "1af7480d-19f0-4b41-b6ef-80318fefdd03",
"name": "Convert to File",
"type": "n8n-nodes-base.convertToFile",
"position": [
-448,
576
],
"parameters": {
"options": {},
"operation": "toBinary",
"sourceProperty": "body.fileContent"
},
"typeVersion": 1.1
},
{
"id": "979be42a-ad72-4e9e-9072-95e585692a98",
"name": "Send a message",
"type": "n8n-nodes-base.slack",
"position": [
2000,
592
],
"parameters": {
"text": "={\n\t\"blocks\": [\n\t\t{\n\t\t\t\"type\": \"header\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"plain_text\",\n\t\t\t\t\"text\": \"\ud83d\udea8 Invoice Requires Manual Review\",\n\t\t\t\t\"emoji\": true\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"fields\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Invoice Number:*\\n{{$json.invoice.number}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Status:*\\n{{$json.exception.status}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Vendor:*\\n{{$json.vendor.name}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Customer:*\\n{{$json.customer.name}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Invoice Date:*\\n{{$json.invoice.issue_date}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Due Date:*\\n{{$json.invoice.due_date}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"fields\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Currency:*\\n{{$json.invoice.currency}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Total Amount:*\\n{{$json.amounts.total}} {{$json.invoice.currency}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Queue:*\\n{{$json.exception.queue}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Raised At:*\\n{{$json.exception.raised_at}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\"text\": \"*Validation Failure(s):*\\n\u2022 {{$json.exception.reasons.join('\\n\u2022 ')}}\"\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"actions\",\n\t\t\t\"block_id\": \"invoice_review_actions\",\n\t\t\t\"elements\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"button\",\n\t\t\t\t\t\"action_id\": \"approve_invoice\",\n\t\t\t\t\t\"text\": {\n\t\t\t\t\t\t\"type\": \"plain_text\",\n\t\t\t\t\t\t\"text\": \"\u2705 Approve\",\n\t\t\t\t\t\t\"emoji\": true\n\t\t\t\t\t},\n\t\t\t\t\t\"style\": \"primary\",\n\t\t\t\t\t\"value\": \"{{$json.invoice.number}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"button\",\n\t\t\t\t\t\"action_id\": \"reject_invoice\",\n\t\t\t\t\t\"text\": {\n\t\t\t\t\t\t\"type\": \"plain_text\",\n\t\t\t\t\t\t\"text\": \"\u274c Reject\",\n\t\t\t\t\t\t\"emoji\": true\n\t\t\t\t\t},\n\t\t\t\t\t\"style\": \"danger\",\n\t\t\t\t\t\"value\": \"{{$json.invoice.number}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"type\": \"context\",\n\t\t\t\"elements\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"Please review this invoice before ERP posting.\"\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}",
"user": {
"__rl": true,
"mode": "list",
"value": "U0BM3KHD9GF",
"cachedResultName": "jaindeepesh205"
},
"select": "user",
"blocksUi": "={\n\t\"blocks\": [\n\t\t{\n\t\t\t\"type\": \"header\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"plain_text\",\n\t\t\t\t\"text\": \"\ud83d\udea8 Invoice Requires Manual Review\",\n\t\t\t\t\"emoji\": true\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"fields\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Invoice Number:*\\n{{$json.invoice.number}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Status:*\\n{{$json.exception.status}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Vendor:*\\n{{$json.vendor.name}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Customer:*\\n{{$json.customer.name}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Invoice Date:*\\n{{$json.invoice.issue_date}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Due Date:*\\n{{$json.invoice.due_date}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"fields\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Currency:*\\n{{$json.invoice.currency}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Total Amount:*\\n{{$json.amounts.total}} {{$json.invoice.currency}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Queue:*\\n{{$json.exception.queue}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"*Raised At:*\\n{{$json.exception.raised_at}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\"text\": \"*Validation Failure(s):*\\n\u2022 {{$json.exception.reasons.join('\\n\u2022 ')}}\"\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"actions\",\n\t\t\t\"block_id\": \"invoice_review_actions\",\n\t\t\t\"elements\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"button\",\n\t\t\t\t\t\"action_id\": \"approve_invoice\",\n\t\t\t\t\t\"text\": {\n\t\t\t\t\t\t\"type\": \"plain_text\",\n\t\t\t\t\t\t\"text\": \"\u2705 Approve\",\n\t\t\t\t\t\t\"emoji\": true\n\t\t\t\t\t},\n\t\t\t\t\t\"style\": \"primary\",\n\t\t\t\t\t\"value\": \"{{$json.invoice.number}}\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"button\",\n\t\t\t\t\t\"action_id\": \"reject_invoice\",\n\t\t\t\t\t\"text\": {\n\t\t\t\t\t\t\"type\": \"plain_text\",\n\t\t\t\t\t\t\"text\": \"\u274c Reject\",\n\t\t\t\t\t\t\"emoji\": true\n\t\t\t\t\t},\n\t\t\t\t\t\"style\": \"danger\",\n\t\t\t\t\t\"value\": \"{{$json.invoice.number}}\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"type\": \"context\",\n\t\t\t\"elements\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\t\"text\": \"Please review this invoice before ERP posting.\"\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}",
"messageType": "block",
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "7195bd8d-6c09-49eb-b3ba-03ce8d960a45",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
1040,
544
],
"parameters": {
"jsCode": "const input = $input.item;\n\n// Extract the structured invoice returned by the AI\nconst invoice = input.json.output[0].content[0].text;\n\nreturn [{\n json: {\n ...invoice\n },\n binary: input.binary\n}];"
},
"typeVersion": 2
},
{
"id": "5b3077d0-c36b-436c-bae8-3334386fd5c8",
"name": "Note - Secure Intake1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
432
],
"parameters": {
"color": 5,
"width": 760,
"height": 340,
"content": "\ud83d\udfe6 1. Secure Intake & Document Validation"
},
"typeVersion": 1
},
{
"id": "e54039fb-6454-4f30-a44d-b4af5b772a22",
"name": "Note - Evidence & Idempotency1",
"type": "n8n-nodes-base.stickyNote",
"position": [
96,
-192
],
"parameters": {
"color": 4,
"width": 438,
"height": 594,
"content": "## 2. Evidence Store & Duplicate Detection\n\nEnsures the same invoice cannot be processed multiple times.\n\n### Responsibilities\n\u2022 Generate document fingerprint\n\u2022 Lookup previous submissions\n\u2022 Maintain immutable evidence\n\u2022 Preserve idempotency\n\u2022 Detect duplicate invoices\n\nProduction Recommendation\nReplace demo duplicate logic with:\n\u2022 PostgreSQL UNIQUE(source_sha256)\nor\n\u2022 Redis SETNX\n\nDuplicate invoices bypass processing and enter the review queue."
},
"typeVersion": 1
},
{
"id": "defdd7e9-e47a-4e68-8f28-d5e9767108f2",
"name": "Extract from File (OCR)",
"type": "n8n-nodes-base.extractFromFile",
"position": [
592,
544
],
"parameters": {
"options": {},
"operation": "pdf",
"binaryPropertyName": "={{ Object.keys($binary)[0] }}"
},
"typeVersion": 1.1
},
{
"id": "eb47c423-fabe-4dd2-b66c-167d8ccff1ba",
"name": "Return Key Information",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
752,
544
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "meta-llama/llama-3.1-70b-instruct",
"cachedResultName": "META-LLAMA/LLAMA-3.1-70B-INSTRUCT"
},
"options": {},
"responses": {
"values": [
{
"role": "system",
"content": "=You are an Enterprise Intelligent Document Processing (IDP) engine specialized in invoice extraction.\n\nYour responsibility is to accurately extract structured invoice information from OCR text.\n\nRules:\n- Extract only information explicitly present in the document.\n- Never hallucinate or fabricate values.\n- If a value is not present, return null.\n- Return exactly one valid JSON object.\n- Do not include markdown, explanations, comments, or code fences.\n- Convert dates to ISO format (YYYY-MM-DD).\n- Convert monetary values to numbers (remove currency symbols and commas).\n- Convert quantities to integers where applicable.\n- Preserve the order of line items.\n- Detect vendor, customer, invoice details, payment terms, taxes, totals, and all line items.\n- Estimate confidence scores between 0.00 and 1.00 for important extracted fields."
},
{
"content": "=Extract the following OCR text into a structured invoice JSON.\n\nOCR DOCUMENT\n\n{{ $json.text }}\n\nReturn ONLY a valid JSON object matching the schema below.\n\n{\n \"document_type\": \"invoice\",\n\n \"vendor\": {\n \"id\": null,\n \"name\": null,\n \"address\": null,\n \"email\": null,\n \"phone\": null,\n \"tax_id\": null\n },\n\n \"customer\": {\n \"name\": null,\n \"address\": null\n },\n\n \"invoice\": {\n \"number\": null,\n \"issue_date\": null,\n \"due_date\": null,\n \"currency\": null,\n \"payment_terms\": null,\n \"purchase_order\": null\n },\n\n \"amounts\": {\n \"subtotal\": null,\n \"tax\": null,\n \"discount\": null,\n \"shipping\": null,\n \"total\": null\n },\n\n \"line_items\": [\n {\n \"line_no\": null,\n \"description\": null,\n \"quantity\": null,\n \"unit_price\": null,\n \"amount\": null\n }\n ],\n\n \"confidence\": {\n \"vendor_name\": null,\n \"invoice_number\": null,\n \"invoice_date\": null,\n \"due_date\": null,\n \"currency\": null,\n \"subtotal\": null,\n \"tax\": null,\n \"total_amount\": null\n }\n}\n\nValidation Rules\n\n- document_type must always be \"invoice\".\n- Monetary values must be numeric.\n- Dates must be YYYY-MM-DD.\n- Currency must be the ISO currency code (USD, INR, EUR, etc.).\n- If an email or phone number is not present, return null.\n- If no discount or shipping charge exists, return 0.\n- Do not omit any fields.\n- Return only JSON."
}
]
},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "80819509-8e7e-4afd-87dc-bb6928ad0a98",
"name": "Note - AI Control1",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
-192
],
"parameters": {
"color": 3,
"width": 620,
"height": 596,
"content": "## 3. OCR & AI Invoice Extraction\n\nExtracts invoice information using OCR and AI while enforcing structured output.\n\n### AI Extraction\n\u2022 Vendor\n\u2022 Customer\n\u2022 Invoice Details\n\u2022 Line Items\n\u2022 Currency\n\u2022 Tax\n\u2022 Totals\n\u2022 Confidence Scores\n\n### AI Controls\n\u2022 Approved LLM\n\u2022 Fixed Prompt Version\n\u2022 JSON Schema Output\n\u2022 Model Provenance\n\u2022 Confidence Tracking\n\nOutput is validated before ERP integration."
},
"typeVersion": 1
},
{
"id": "cc1c3e27-989e-4829-a965-32ba02319ef6",
"name": "Note - Human Review1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1744,
-192
],
"parameters": {
"color": 6,
"width": 1124,
"height": 588,
"content": "## 5. Human Review & Exception Handling\n\nRoutes invoices requiring manual verification before ERP posting.\n\n### Review Triggers\n\u2022 Low AI confidence\n\u2022 Validation failures\n\u2022 Duplicate documents\n\u2022 Invalid intake\n\u2022 ERP retry exhaustion\n\n### Reviewer Actions\n\u2714 Approve\n\u2714 Reject\n\u2714 Add Correction Reason\n\u2714 Capture Reviewer ID\n\nAll review actions are fully audited."
},
"typeVersion": 1
},
{
"id": "fe11a587-f6c7-4d98-adc4-7b8d5f0a5c28",
"name": "Note - AI Control3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1184,
-192
],
"parameters": {
"width": 540,
"height": 596,
"content": "## 4. Business Validation Engine\n\nValidates extracted invoice data using deterministic business rules.\n\n### Validation Rules\n\u2022 Vendor Exists\n\u2022 Invoice Number\n\u2022 Invoice Date\n\u2022 Due Date\n\u2022 Currency\n\u2022 Tax\n\u2022 Total Amount\n\u2022 Line Items\n\u2022 Confidence Threshold\n\nEach failure produces a standardized validation code.\n\nExamples\nVAL-01 Vendor Missing\nVAL-03 Invoice Number Missing\nVAL-09 Invalid Total\nVAL-13 Low Confidence"
},
"typeVersion": 1
},
{
"id": "0979e966-669c-4f06-ac98-2e71122b9882",
"name": "Note - AI Control4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1184,
432
],
"parameters": {
"width": 540,
"height": 340,
"content": "\ud83d\udfe8 4. Business Validation Engine"
},
"typeVersion": 1
},
{
"id": "aa148761-892b-4efb-915b-0f960031d03e",
"name": "Note - ERP & Recovery1",
"type": "n8n-nodes-base.stickyNote",
"position": [
2880,
432
],
"parameters": {
"color": 2,
"width": 692,
"height": 339,
"content": "\ud83d\udfe7 6. ERP Posting & Recovery"
},
"typeVersion": 1
},
{
"id": "d988542a-c215-4a42-bf64-378e8b238b96",
"name": "Note - Deployment Checklist1",
"type": "n8n-nodes-base.stickyNote",
"position": [
32,
816
],
"parameters": {
"color": "#FFF0FF",
"width": 674,
"height": 389,
"content": "## 7. Audit Logging & Compliance\n\nCreates a sanitized audit trail without storing document contents.\n\nCaptured Information\n\u2022 Correlation ID\n\u2022 Processing Status\n\u2022 Validation Result\n\u2022 Duplicate Status\n\u2022 Review Decision\n\u2022 ERP Posting Result\n\u2022 AI Model Version\n\nSupports compliance, troubleshooting, and traceability."
},
"typeVersion": 1
},
{
"id": "7fe09c8f-361f-4e49-9c28-a589b809ed5b",
"name": "Note - Secure Intake2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-992
],
"parameters": {
"color": "#E8F8F6",
"width": 2584,
"height": 772,
"content": "# Intelligent Invoice Processing Workflow\n\n## Purpose\n\nThis workflow automates the complete invoice processing lifecycle by securely ingesting invoices, extracting structured information using AI, validating business rules, routing exceptions for human review, and posting approved invoices to the ERP system.\n\nThe solution minimizes manual effort, reduces processing time, prevents duplicate invoice creation, and maintains a complete audit trail for compliance and operational transparency.\n\n---\n\n## Business Objectives\n\n\u2022 Automate invoice processing from document upload to ERP posting.\n\u2022 Eliminate duplicate invoice processing using immutable evidence.\n\u2022 Improve extraction accuracy through AI-assisted document understanding.\n\u2022 Enforce deterministic business validation before ERP integration.\n\u2022 Enable Human-in-the-Loop review for low-confidence or exceptional cases.\n\u2022 Maintain secure, traceable, and auditable processing.\n\u2022 Ensure reliable ERP integration using idempotent requests and recovery mechanisms.\n\n---\n\n## Expected Outcome\n\u2714 Faster invoice processing\n\u2714 Reduced manual intervention\n\u2714 Higher extraction accuracy\n\u2714 Controlled exception handling\n\u2714 Complete auditability\n\u2714 Enterprise-ready document processing"
},
"typeVersion": 1
},
{
"id": "9be070a9-118e-43a7-8538-6d9c72c66c5a",
"name": "Note - Secure Intake3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1744,
-592
],
"parameters": {
"color": "#F5EBEB",
"width": 1000,
"height": 1796,
"content": "# Workflow Operation Guide\n\n## Step 1 \u2013 Configure Credentials\n\nBefore execution, configure:\n\n\u2022 AI Model Credentials\n\u2022 ERP API Credentials\n\u2022 Slack / Teams Notification Credentials\n\u2022 Database Credentials\n\u2022 Storage Credentials (if applicable)\n\n---\n\n## Step 2 \u2013 Submit Invoice\n\nSend an invoice using the webhook endpoint.\n\nSupported formats:\n\u2022 PDF\n\u2022 PNG\n\u2022 JPG\n\nThe workflow automatically validates the uploaded document.\n\n---\n\n## Step 3 \u2013 Automated Processing\n\nThe workflow automatically performs:\n\n\u2022 Intake Validation\n\u2022 Duplicate Detection\n\u2022 OCR Extraction\n\u2022 AI Data Extraction\n\u2022 Business Validation\n\nNo manual intervention is required during this stage.\n\n---\n\n## Step 4 \u2013 Human Review (If Required)\n\nInvoices are routed to the review queue when:\n\n\u2022 AI confidence is below threshold\n\u2022 Validation rules fail\n\u2022 Duplicate documents are detected\n\u2022 Intake validation fails\n\nThe reviewer can:\n\n\u2714 Approve\n\u2714 Reject\n\u2714 Provide correction reason\n\u2714 Record reviewer comments\n\n---\n\n## Step 5 \u2013 ERP Posting\n\nApproved invoices are posted to the ERP system using an idempotent API request.\nSuccessful posting generates an audit record.\n\n---\n\n## Step 6 \u2013 Audit & Monitoring\n\nAfter completion the workflow stores:\n\n\u2022 Correlation ID\n\u2022 Processing Status\n\u2022 Validation Result\n\u2022 Review Decision\n\u2022 ERP Response\n\u2022 Processing Timestamp\n\u2022 AI Model Information\n\nThese records support monitoring, troubleshooting, and compliance reporting."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "4a453b69-426f-460d-9c5b-93db54ecf52f",
"nodeGroups": [],
"connections": {
"Send a message": {
"main": [
[
{
"node": "Wait for Human Review",
"type": "main",
"index": 0
}
]
]
},
"Convert to File": {
"main": [
[
{
"node": "Validate File & Create Metadata",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Validation Policy Engine",
"type": "main",
"index": 0
}
]
]
},
"Route Intake Status": {
"main": [
[
{
"node": "Duplicate Check (Replace With DB)",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Exception Record",
"type": "main",
"index": 0
}
]
]
},
"Route Review Decision": {
"main": [
[
{
"node": "Idempotent ERP Post (Configure Credential)",
"type": "main",
"index": 0
}
],
[
{
"node": "Record Rejection / Duplicate",
"type": "main",
"index": 0
}
]
]
},
"Wait for Human Review": {
"main": [
[
{
"node": "Validate Review Decision",
"type": "main",
"index": 0
}
]
]
},
"Return Key Information": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Route Duplicate Status": {
"main": [
[
{
"node": "Extract from File (OCR)",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Exception Record",
"type": "main",
"index": 0
}
]
]
},
"Route by Policy Status": {
"main": [
[
{
"node": "Idempotent ERP Post (Configure Credential)",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Exception Record",
"type": "main",
"index": 0
}
]
]
},
"Create Exception Record": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Extract from File (OCR)": {
"main": [
[
{
"node": "Return Key Information",
"type": "main",
"index": 0
}
]
]
},
"Validate Review Decision": {
"main": [
[
{
"node": "Route Review Decision",
"type": "main",
"index": 0
}
]
]
},
"Validation Policy Engine": {
"main": [
[
{
"node": "Route by Policy Status",
"type": "main",
"index": 0
}
]
]
},
"Ingest Document (Webhook)": {
"main": [
[
{
"node": "Convert to File",
"type": "main",
"index": 0
}
]
]
},
"Validate File & Create Metadata": {
"main": [
[
{
"node": "Route Intake Status",
"type": "main",
"index": 0
}
]
]
},
"Duplicate Check (Replace With DB)": {
"main": [
[
{
"node": "Route Duplicate Status",
"type": "main",
"index": 0
}
]
]
},
"Idempotent ERP Post (Configure Credential)": {
"main": [
[
{
"node": "Generate Sanitized Audit Log",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
openAiApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow accepts invoice documents via webhook, validates and de-duplicates them, extracts invoice data from PDFs with OpenAI, and either posts approved invoices to an ERP via HTTP or routes exceptions to Slack for human review before continuing. Receives a POST request on…
Source: https://n8n.io/workflows/17806/ — 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.
This workflow captures new leads via webhook, enriches and scores them with Apollo and Google Gemini, logs everything in Google Sheets, and automates outreach, reply handling, follow-ups, meeting prep
Venafi Presentation - Watch Video
Automatically detects missed Zoom demos booked via Calendly and triggers AI-powered follow-up sequences.
AI-Powered Fake Review Detection Workflow Using n8n & Airtable. Uses httpRequest, airtable, openAi, slack. Webhook trigger; 27 nodes.
This workflow automates the end-to-end process of scheduling technical or behavioral interviews. It captures interview data via Webhook, creates a Google Calendar event with an integrated Google Meet