AutomationFlowsWeb Scraping › Extract Invoice Data From PDF with Claude AI and Create Vendor Bill in Odoo 18

Extract Invoice Data From PDF with Claude AI and Create Vendor Bill in Odoo 18

ByFlorian Eiche @jagged on n8n.io

This workflow automates accounts payable: upload a PDF invoice, let Claude AI extract the key fields, and automatically create a vendor bill (incoming invoice) in Odoo 18.

Webhook trigger★★★★☆ complexity29 nodesHTTP Request
Web Scraping Trigger: Webhook Nodes: 29 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #14573 — we link there as the canonical source.

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 →

Download .json
{
  "id": "jjHhqwplpaGh6V3b",
  "name": "Extract invoice data from PDF with Claude AI and create vendor bill in Odoo 18",
  "tags": [],
  "nodes": [
    {
      "id": "0bd4b266-d85d-40c5-bbea-278d23dba904",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        768
      ],
      "parameters": {
        "color": "#FFF5D6",
        "width": 940,
        "height": 1072,
        "content": "## Extract Invoice Data from PDF with Claude AI and Create Vendor Bill in Odoo 18\n\nThis workflow automates **accounts payable**: upload a PDF invoice, let Claude AI extract the key fields, and automatically create a vendor bill (incoming invoice) in Odoo 18.\n\n## Who is this for?\nSmall and medium businesses using Odoo 18 who want to automate invoice data entry. Finance teams, bookkeepers, or anyone processing incoming invoices manually.\n\n## How it works\n1. Receive a PDF invoice via webhook (file upload)\n2. Convert the PDF to base64 for AI processing\n3. Claude AI extracts: vendor name, invoice number, date, amounts, tax rate\n4. Authenticate with Odoo 18 via JSON-RPC API\n5. Check for duplicate invoices (by invoice number)\n6. Find or create the vendor (res.partner) in Odoo\n7. Create a **draft** vendor bill (account.move) in Odoo\n8. Return a structured JSON response with the result\n\nThe invoice is created in **draft status** so you can review and confirm it manually in Odoo before posting.\n\n## Extracted fields\n`vendor`, `invoice_number`, `invoice_date`, `total_amount`, `currency`, `net_amount`, `tax_rate`, `tax_amount`\n\n## Setup\n- [ ] Open the **Configuration** node and enter your Odoo URL, database, user, and password\n- [ ] Create an **HTTP Header Auth** credential named `Anthropic API Key` with header name `x-api-key` and your Anthropic API key as value\n- [ ] Activate the workflow and send a test PDF\n\n## Test with curl\n```\ncurl -X POST https://your-n8n-instance.com/webhook/invoice-process -F \"data=@invoice.pdf\"\n```\n\n## Requirements\n- Odoo 18 with Invoicing module installed\n- Anthropic API key (Claude Sonnet 4.5 recommended)\n- n8n 2.x (self-hosted or cloud)\n\n## How to customize\n- Change the AI model in the **Configuration** node (e.g. `claude-haiku-4-5-20251001` for lower cost)\n- Modify the extraction prompt in **Prepare Claude Request** to add custom fields\n- Add a file storage step after invoice creation (e.g. Google Drive, S3, WebDAV)\n- Connect an email trigger instead of the webhook for fully automated processing\n\n## Note on data privacy\nThis workflow sends PDF documents to the Anthropic API for data extraction. Ensure you have a [Data Processing Agreement (DPA)](https://www.anthropic.com/legal/data-processing-addendum) with Anthropic and comply with your local data protection regulations (e.g. GDPR) before processing documents containing personal data.\n\n**Author:** Florian Eiche \u2014 [eiche-digital.de](https://eiche-digital.de)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c3c7da18-0bac-49d8-9562-f768a3b4b244",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        1232
      ],
      "parameters": {
        "color": "#737373",
        "width": 1340,
        "height": 312,
        "content": "## 1. PDF Upload & AI Extraction\nThe webhook receives a PDF file upload. The file is converted to base64, then sent to Claude AI with a structured extraction prompt. Claude returns JSON with all invoice fields."
      },
      "typeVersion": 1
    },
    {
      "id": "45f83c7f-4d07-47f9-945d-bc6a24de23e1",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        1232
      ],
      "parameters": {
        "color": "#737373",
        "width": 1172,
        "height": 312,
        "content": "## 2. Odoo Authentication & Duplicate Check\nAuthenticate via JSON-RPC, then search for existing invoices with the same reference number. If a duplicate is found, the workflow returns a clean response instead of creating a duplicate."
      },
      "typeVersion": 1
    },
    {
      "id": "10c75ce5-79a5-4600-a7c8-0d20f7b327a2",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2544,
        1232
      ],
      "parameters": {
        "color": "#737373",
        "width": 1404,
        "height": 568,
        "content": "## 3. Vendor Lookup & Creation\nSearch for the vendor by name in Odoo. If found, use the existing partner. If not, create a new vendor (res.partner) with `supplier_rank: 1`."
      },
      "typeVersion": 1
    },
    {
      "id": "b75880da-c3df-4e65-b7cb-d366d1da377b",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3968,
        1232
      ],
      "parameters": {
        "color": "#737373",
        "width": 1044,
        "height": 568,
        "content": "## 4. Invoice Creation & Response\nCreate a draft vendor bill (account.move with move_type `in_invoice`) in Odoo. The invoice is created with one line containing the net amount. Returns a JSON response with the Odoo invoice ID."
      },
      "typeVersion": 1
    },
    {
      "id": "bf398631-cba8-45af-9934-38ef17a3d88c",
      "name": "Receive Invoice PDF",
      "type": "n8n-nodes-base.webhook",
      "position": [
        64,
        1376
      ],
      "parameters": {
        "path": "invoice-process",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "057afa8e-248a-452e-8894-96ece1c0a752",
      "name": "PDF to Base64",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        320,
        1376
      ],
      "parameters": {
        "options": {},
        "operation": "binaryToPropery"
      },
      "typeVersion": 1
    },
    {
      "id": "eb9426eb-cc78-473c-9bab-21aa3a736b99",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        592,
        1376
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"odooUrl\": \"https://your-odoo-instance.com\",\n  \"odooDb\": \"your_database_name\",\n  \"odooUser\": \"admin\",\n  \"odooPassword\": \"your_password_here\",\n  \"anthropicModel\": \"claude-sonnet-4-5-20250929\"\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "67de80c2-861b-4127-8f1e-bd5cc6b3a6d4",
      "name": "Prepare Claude Request",
      "type": "n8n-nodes-base.code",
      "position": [
        864,
        1376
      ],
      "parameters": {
        "jsCode": "// Build Anthropic API request with PDF as base64 document\nconst base64Item = $('PDF to Base64').first().json;\nconst pdfBase64 = base64Item.data;\n\nif (!pdfBase64 || pdfBase64.length < 100) {\n  throw new Error('No valid PDF data received. Length: ' + (pdfBase64 ? pdfBase64.length : 0));\n}\n\nconst config = $('Configuration').first().json;\n\nconst requestBody = {\n  model: config.anthropicModel || 'claude-sonnet-4-5-20250929',\n  max_tokens: 1024,\n  messages: [{\n    role: 'user',\n    content: [\n      {\n        type: 'document',\n        source: {\n          type: 'base64',\n          media_type: 'application/pdf',\n          data: pdfBase64\n        }\n      },\n      {\n        type: 'text',\n        text: 'Extract the following fields from this invoice and return ONLY valid JSON, no markdown, no explanation:\\n\\n{\\n  \"vendor\": \"Name of the vendor/sender\",\\n  \"invoice_number\": \"Invoice number\",\\n  \"invoice_date\": \"YYYY-MM-DD\",\\n  \"total_amount\": 0.00,\\n  \"currency\": \"EUR\",\\n  \"net_amount\": 0.00,\\n  \"tax_rate\": 19,\\n  \"tax_amount\": 0.00\\n}\\n\\nIf a field is not found, set it to null.'\n      }\n    ]\n  }]\n};\n\nreturn [{ json: { requestBody } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "5fba2aef-0212-4817-b5d4-c89e9011ee95",
      "name": "Claude AI Extract",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1152,
        1376
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify($json.requestBody) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4fffd0e5-cbd0-4ecf-a400-b4bdf41d29c8",
      "name": "Parse Extraction",
      "type": "n8n-nodes-base.code",
      "position": [
        1392,
        1376
      ],
      "parameters": {
        "jsCode": "// Parse and validate Claude AI response\nconst response = $input.first().json;\n\nif (response.error) {\n  throw new Error('Claude API error: ' + (response.error.message || JSON.stringify(response.error)));\n}\n\nconst text = response.content?.[0]?.text || '';\n\nlet invoice;\ntry {\n  const cleaned = text.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n  invoice = JSON.parse(cleaned);\n} catch (e) {\n  throw new Error('Claude response is not valid JSON: ' + text.substring(0, 300));\n}\n\n// Validate required fields\nconst required = ['vendor', 'invoice_number', 'invoice_date', 'total_amount'];\nconst missing = required.filter(f => !invoice[f] && invoice[f] !== 0);\nif (missing.length > 0) {\n  throw new Error('Missing required fields: ' + missing.join(', '));\n}\n\n// Normalize numeric values\ninvoice.total_amount = parseFloat(invoice.total_amount);\ninvoice.net_amount = invoice.net_amount ? parseFloat(invoice.net_amount) : invoice.total_amount;\ninvoice.tax_amount = invoice.tax_amount ? parseFloat(invoice.tax_amount) : 0;\ninvoice.tax_rate = invoice.tax_rate ? parseFloat(invoice.tax_rate) : 0;\ninvoice.currency = invoice.currency || 'EUR';\n\nreturn [{ json: invoice }];"
      },
      "typeVersion": 2
    },
    {
      "id": "3011122b-d5df-4492-a4ae-9703aed98373",
      "name": "Odoo Authenticate",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1648,
        1376
      ],
      "parameters": {
        "url": "={{ $('Configuration').first().json.odooUrl }}/jsonrpc",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ jsonrpc: '2.0', method: 'call', params: { service: 'common', method: 'authenticate', args: [$('Configuration').first().json.odooDb, $('Configuration').first().json.odooUser, $('Configuration').first().json.odooPassword, {}] }, id: 1 }) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "72c9312e-7ed7-4131-a1d5-b342cf5b3386",
      "name": "Prepare Duplicate Check",
      "type": "n8n-nodes-base.code",
      "position": [
        1904,
        1376
      ],
      "parameters": {
        "jsCode": "// Verify Odoo auth and prepare duplicate check\nconst authResult = $input.first().json;\nif (!authResult.result || authResult.error) {\n  throw new Error('Odoo authentication failed: ' + JSON.stringify(authResult.error || 'uid is null'));\n}\n\nconst uid = authResult.result;\nconst config = $('Configuration').first().json;\nconst invoice = $('Parse Extraction').first().json;\n\n// Search for existing invoice with same reference number\nconst dupCheckBody = {\n  jsonrpc: '2.0',\n  method: 'call',\n  params: {\n    service: 'object',\n    method: 'execute_kw',\n    args: [\n      config.odooDb,\n      uid,\n      config.odooPassword,\n      'account.move',\n      'search_read',\n      [[['ref', '=', invoice.invoice_number], ['move_type', '=', 'in_invoice']]],\n      { fields: ['id', 'ref', 'partner_id', 'state', 'amount_total'], limit: 1 }\n    ]\n  },\n  id: 10\n};\n\nreturn [{ json: { uid, dupCheckBody, invoice } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "f3fe614e-b5d0-4a65-bf9d-124db82c9384",
      "name": "Search Duplicate Invoice",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2144,
        1376
      ],
      "parameters": {
        "url": "={{ $('Configuration').first().json.odooUrl }}/jsonrpc",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify($json.dupCheckBody) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "50f05a19-ad7d-4596-befd-5612e8781bba",
      "name": "Duplicate Found?",
      "type": "n8n-nodes-base.if",
      "position": [
        2384,
        1376
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition-duplicate-exists",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.result.length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ebbb50ce-d57a-4876-b44f-4557ef378cd5",
      "name": "Handle Duplicate",
      "type": "n8n-nodes-base.code",
      "position": [
        2624,
        1376
      ],
      "parameters": {
        "jsCode": "// Duplicate found \u2014 return clean response\nconst dupResult = $('Search Duplicate Invoice').first().json.result[0];\nconst invoice = $('Prepare Duplicate Check').first().json.invoice;\n\nreturn [{\n  json: {\n    success: false,\n    duplicate: true,\n    message: 'Invoice ' + invoice.invoice_number + ' already exists in Odoo.',\n    existing_invoice: {\n      odoo_id: dupResult.id,\n      status: dupResult.state,\n      amount: dupResult.amount_total,\n      vendor: dupResult.partner_id ? dupResult.partner_id[1] : null\n    },\n    extracted_data: invoice\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "abac0d06-aaea-43d7-bf2c-3cc833de45a0",
      "name": "Prepare Vendor Search",
      "type": "n8n-nodes-base.code",
      "position": [
        2624,
        1536
      ],
      "parameters": {
        "jsCode": "// No duplicate \u2014 prepare vendor search\nconst prev = $('Prepare Duplicate Check').first().json;\nconst config = $('Configuration').first().json;\n\nconst searchBody = {\n  jsonrpc: '2.0',\n  method: 'call',\n  params: {\n    service: 'object',\n    method: 'execute_kw',\n    args: [\n      config.odooDb,\n      prev.uid,\n      config.odooPassword,\n      'res.partner',\n      'search_read',\n      [[['name', 'ilike', prev.invoice.vendor], ['supplier_rank', '>', 0]]],\n      { fields: ['id', 'name'], limit: 1 }\n    ]\n  },\n  id: 2\n};\n\nreturn [{ json: { uid: prev.uid, searchBody, invoice: prev.invoice } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "eaff093f-d675-4a21-bd52-76df146eb9b6",
      "name": "Search Vendor",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2864,
        1536
      ],
      "parameters": {
        "url": "={{ $('Configuration').first().json.odooUrl }}/jsonrpc",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify($json.searchBody) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "daeb5b81-3552-4797-bfa7-0b1bc8c26d1b",
      "name": "Vendor Exists?",
      "type": "n8n-nodes-base.if",
      "position": [
        3104,
        1376
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition-vendor-exists",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.result.length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ffd976ba-0a8c-4774-9adf-592736a0fcfd",
      "name": "Use Existing Vendor",
      "type": "n8n-nodes-base.code",
      "position": [
        3344,
        1360
      ],
      "parameters": {
        "jsCode": "// Use existing vendor\nconst partnerId = $json.result[0].id;\nconst partnerName = $json.result[0].name;\nconst prev = $('Prepare Vendor Search').first().json;\n\nreturn [{\n  json: {\n    partnerId,\n    partnerName,\n    uid: prev.uid,\n    invoice: prev.invoice\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c34dd409-5f06-4417-8cf4-e3537d607158",
      "name": "Prepare Create Vendor",
      "type": "n8n-nodes-base.code",
      "position": [
        3344,
        1488
      ],
      "parameters": {
        "jsCode": "// Vendor not found \u2014 prepare creation\nconst config = $('Configuration').first().json;\nconst prev = $('Prepare Vendor Search').first().json;\n\nconst createBody = {\n  jsonrpc: '2.0',\n  method: 'call',\n  params: {\n    service: 'object',\n    method: 'execute_kw',\n    args: [\n      config.odooDb,\n      prev.uid,\n      config.odooPassword,\n      'res.partner',\n      'create',\n      [{ name: prev.invoice.vendor, supplier_rank: 1, company_type: 'company' }]\n    ]\n  },\n  id: 3\n};\n\nreturn [{ json: { createBody, uid: prev.uid, invoice: prev.invoice } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "29db158d-d227-450f-a8e0-53eaa894a00f",
      "name": "Create Vendor in Odoo",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3584,
        1488
      ],
      "parameters": {
        "url": "={{ $('Configuration').first().json.odooUrl }}/jsonrpc",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify($json.createBody) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "25137c82-5c80-4a18-82c0-9011da543f5e",
      "name": "Get New Vendor ID",
      "type": "n8n-nodes-base.code",
      "position": [
        3792,
        1488
      ],
      "parameters": {
        "jsCode": "// Extract new vendor ID from create response\nconst result = $input.first().json;\nif (!result.result) {\n  throw new Error('Failed to create vendor: ' + JSON.stringify(result.error || result));\n}\n\nconst prev = $('Prepare Create Vendor').first().json;\n\nreturn [{\n  json: {\n    partnerId: result.result,\n    partnerName: prev.invoice.vendor,\n    uid: prev.uid,\n    invoice: prev.invoice\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a33d9edb-8a89-413e-8e64-8fd330a2720a",
      "name": "Prepare Invoice",
      "type": "n8n-nodes-base.code",
      "position": [
        4048,
        1360
      ],
      "parameters": {
        "jsCode": "// Prepare Odoo vendor bill (account.move)\nconst data = $input.first().json;\nconst config = $('Configuration').first().json;\n\nconst invoiceBody = {\n  jsonrpc: '2.0',\n  method: 'call',\n  params: {\n    service: 'object',\n    method: 'execute_kw',\n    args: [\n      config.odooDb,\n      data.uid,\n      config.odooPassword,\n      'account.move',\n      'create',\n      [{\n        move_type: 'in_invoice',\n        partner_id: data.partnerId,\n        ref: data.invoice.invoice_number,\n        invoice_date: data.invoice.invoice_date,\n        invoice_line_ids: [\n          [0, 0, {\n            name: 'Invoice ' + data.invoice.invoice_number + ' - ' + data.invoice.vendor,\n            quantity: 1,\n            price_unit: data.invoice.net_amount\n          }]\n        ]\n      }]\n    ]\n  },\n  id: 4\n};\n\nreturn [{\n  json: {\n    invoiceBody,\n    partnerId: data.partnerId,\n    partnerName: data.partnerName,\n    uid: data.uid,\n    invoice: data.invoice\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "696ad174-97fd-493f-acee-5475b99ca5be",
      "name": "Create Invoice in Odoo",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        4288,
        1504
      ],
      "parameters": {
        "url": "={{ $('Configuration').first().json.odooUrl }}/jsonrpc",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify($json.invoiceBody) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "19a84d9f-9c13-4ed6-942a-5118bb66a8aa",
      "name": "Build Response",
      "type": "n8n-nodes-base.code",
      "position": [
        4528,
        1360
      ],
      "parameters": {
        "jsCode": "// Verify invoice creation and build success response\nconst result = $input.first().json;\nif (!result.result) {\n  throw new Error('Failed to create invoice: ' + JSON.stringify(result.error || result));\n}\n\nconst prev = $('Prepare Invoice').first().json;\n\nreturn [{\n  json: {\n    success: true,\n    odoo_invoice_id: result.result,\n    partner_id: prev.partnerId,\n    partner_name: prev.partnerName,\n    invoice: prev.invoice\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "3ccdd767-939e-4d60-b867-71eb57bac30e",
      "name": "Success Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        4768,
        1360
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "dcedaa35-36c5-4ac0-b990-caf9cc6e7b72",
      "name": "Duplicate Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2864,
        1376
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "d511e2fd-8ca9-441c-8c16-9059f6170ae3",
      "name": "Error Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1808,
        1744
      ],
      "parameters": {
        "options": {
          "responseCode": 500
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: false, error: $json.error?.message || $json.message || 'Unknown error', node: $json.node || 'unknown' }) }}"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "9e93180c-b647-4acd-a020-827012ae57ec",
  "connections": {
    "Configuration": {
      "main": [
        [
          {
            "node": "Prepare Claude Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PDF to Base64": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Vendor": {
      "main": [
        [
          {
            "node": "Vendor Exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Response": {
      "main": [
        [
          {
            "node": "Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Vendor Exists?": {
      "main": [
        [
          {
            "node": "Use Existing Vendor",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Create Vendor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Invoice": {
      "main": [
        [
          {
            "node": "Create Invoice in Odoo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Duplicate Found?": {
      "main": [
        [
          {
            "node": "Handle Duplicate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Vendor Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Duplicate": {
      "main": [
        [
          {
            "node": "Duplicate Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Extraction": {
      "main": [
        [
          {
            "node": "Odoo Authenticate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude AI Extract": {
      "main": [
        [
          {
            "node": "Parse Extraction",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get New Vendor ID": {
      "main": [
        [
          {
            "node": "Prepare Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Odoo Authenticate": {
      "main": [
        [
          {
            "node": "Prepare Duplicate Check",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Invoice PDF": {
      "main": [
        [
          {
            "node": "PDF to Base64",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use Existing Vendor": {
      "main": [
        [
          {
            "node": "Prepare Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Vendor in Odoo": {
      "main": [
        [
          {
            "node": "Get New Vendor ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Create Vendor": {
      "main": [
        [
          {
            "node": "Create Vendor in Odoo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Vendor Search": {
      "main": [
        [
          {
            "node": "Search Vendor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Invoice in Odoo": {
      "main": [
        [
          {
            "node": "Build Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Claude Request": {
      "main": [
        [
          {
            "node": "Claude AI Extract",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Duplicate Check": {
      "main": [
        [
          {
            "node": "Search Duplicate Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Duplicate Invoice": {
      "main": [
        [
          {
            "node": "Duplicate Found?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automates accounts payable: upload a PDF invoice, let Claude AI extract the key fields, and automatically create a vendor bill (incoming invoice) in Odoo 18.

Source: https://n8n.io/workflows/14573/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di

n8n, Execute Workflow Trigger, HTTP Request +1
Web Scraping

This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .

HTTP Request, Ssh
Web Scraping

This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c

HTTP Request
Web Scraping

Sign PDF documents with legally-compliant digital signatures using X.509 certificates. Supports multiple PAdES signature levels (B, T, LT, LTA) with optional visible stamps.

Execute Command, HTTP Request, Read Write File +1
Web Scraping

📡 This workflow serves as the central Alpha Vantage API fetcher for Tesla trading indicators, delivering cleaned 20-point JSON outputs for three timeframes: , , and . It is required by the following a

HTTP Request