Invoice and Receipt Extractor
Module 07 · business · intermediate
learner.json
In n8n, copy this file and paste it onto the canvas, or use Import from File.
{
"name": "07 - Invoice and Receipt Extractor",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "receipt",
"responseMode": "responseNode",
"options": {}
},
"id": "m07-webhook",
"name": "Receipt API",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
100
],
"webhookId": "course-07-receipt"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "b1",
"leftValue": "={{ Object.values($binary ?? {}).some(f => f.mimeType === 'application/pdf') }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "m07-haspdf",
"name": "Has PDF?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
220,
100
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { error: 'upload a PDF as multipart form-data, e.g. curl -F file=@receipt.pdf' } }}",
"options": {
"responseCode": 400
}
},
"id": "m07-400",
"name": "Respond No PDF",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
440,
260
]
},
{
"parameters": {
"operation": "pdf",
"binaryPropertyName": "={{ Object.keys($binary).find(k => $binary[k].mimeType === 'application/pdf') }}",
"options": {
"joinPages": true,
"maxPages": 5
}
},
"id": "m07-extract",
"name": "Extract PDF Text",
"type": "n8n-nodes-base.extractFromFile",
"typeVersion": 1.1,
"position": [
440,
0
]
},
{
"parameters": {
"text": "={{ $json.text.slice(0, 8000) }}",
"schemaType": "fromJson",
"jsonSchemaExample": "{\n \"vendor\": \"Brewlab Coffee Co.\",\n \"invoice_number\": \"INV-1001\",\n \"date\": \"2026-01-31\",\n \"currency\": \"USD\",\n \"subtotal\": 20.5,\n \"tax\": 1.7,\n \"total\": 22.2,\n \"category\": \"meals\",\n \"line_items\": [\n { \"description\": \"Latte\", \"quantity\": 2, \"amount\": 11 }\n ]\n}",
"options": {
"systemPromptTemplate": "You extract data from receipts and invoices. Return dates as YYYY-MM-DD and amounts as plain numbers without currency symbols. currency is a 3-letter ISO code. category is one of: meals, travel, software, office, utilities, other. If a value is missing in the document, use null. Never invent values."
}
},
"id": "m07-ai",
"name": "Extract Receipt Data",
"type": "@n8n/n8n-nodes-langchain.informationExtractor",
"typeVersion": 1.2,
"position": [
660,
0
]
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-5-mini",
"cachedResultName": "gpt-5-mini"
},
"options": {}
},
"id": "m07-model",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.3,
"position": [
660,
220
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "c1",
"name": "vendor",
"value": "={{ $json.output.vendor ?? '' }}",
"type": "string"
},
{
"id": "c2",
"name": "invoice_number",
"value": "={{ $json.output.invoice_number ?? '' }}",
"type": "string"
},
{
"id": "c3",
"name": "date",
"value": "={{ $json.output.date ?? '' }}",
"type": "string"
},
{
"id": "c4",
"name": "currency",
"value": "={{ ($json.output.currency ?? '').toUpperCase() }}",
"type": "string"
},
{
"id": "c5",
"name": "subtotal",
"value": "={{ Number($json.output.subtotal ?? 0) }}",
"type": "number"
},
{
"id": "c6",
"name": "tax",
"value": "={{ Number($json.output.tax ?? 0) }}",
"type": "number"
},
{
"id": "c7",
"name": "total",
"value": "={{ Number($json.output.total ?? 0) }}",
"type": "number"
},
{
"id": "c8",
"name": "category",
"value": "={{ $json.output.category ?? 'other' }}",
"type": "string"
},
{
"id": "c9",
"name": "items_count",
"value": "={{ ($json.output.line_items ?? []).length }}",
"type": "number"
},
{
"id": "c10",
"name": "line_items",
"value": "={{ $json.output.line_items ?? [] }}",
"type": "array"
},
{
"id": "c11",
"name": "needs_review",
"value": "={{ !$json.output.total || !$json.output.date || !$json.output.vendor }}",
"type": "boolean"
},
{
"id": "c12",
"name": "processed_at",
"value": "={{ $now.toISO() }}",
"type": "string"
}
]
},
"options": {}
},
"id": "m07-clean",
"name": "Clean Receipt",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
880,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "d1",
"leftValue": "={{ $('Receipt API').item.json.query.dry_run === 'true' }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "m07-dry",
"name": "Dry Run?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1100,
0
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit"
},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Expenses"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"date": "={{ $json.date }}",
"vendor": "={{ $json.vendor }}",
"invoice_number": "={{ $json.invoice_number }}",
"category": "={{ $json.category }}",
"currency": "={{ $json.currency }}",
"subtotal": "={{ $json.subtotal }}",
"tax": "={{ $json.tax }}",
"total": "={{ $json.total }}",
"needs_review": "={{ $json.needs_review }}",
"processed_at": "={{ $json.processed_at }}"
},
"matchingColumns": [],
"schema": [
{
"id": "date",
"displayName": "date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "vendor",
"displayName": "vendor",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "invoice_number",
"displayName": "invoice_number",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "category",
"displayName": "category",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "currency",
"displayName": "currency",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "subtotal",
"displayName": "subtotal",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "tax",
"displayName": "tax",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "total",
"displayName": "total",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "needs_review",
"displayName": "needs_review",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "processed_at",
"displayName": "processed_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
]
},
"options": {}
},
"id": "m07-sheets",
"name": "Save to Expenses",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
1320,
100
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { saved: $(\"Receipt API\").item.json.query.dry_run !== \"true\", ...$(\"Clean Receipt\").item.json } }}",
"options": {}
},
"id": "m07-respond",
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1540,
0
]
}
],
"connections": {
"Receipt API": {
"main": [
[
{
"node": "Has PDF?",
"type": "main",
"index": 0
}
]
]
},
"Has PDF?": {
"main": [
[
{
"node": "Extract PDF Text",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond No PDF",
"type": "main",
"index": 0
}
]
]
},
"Extract PDF Text": {
"main": [
[
{
"node": "Extract Receipt Data",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Extract Receipt Data",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Extract Receipt Data": {
"main": [
[
{
"node": "Clean Receipt",
"type": "main",
"index": 0
}
]
]
},
"Clean Receipt": {
"main": [
[
{
"node": "Dry Run?",
"type": "main",
"index": 0
}
]
]
},
"Dry Run?": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
],
[
{
"node": "Save to Expenses",
"type": "main",
"index": 0
}
]
]
},
"Save to Expenses": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
}
}