AutomationFlowsAI & RAG › Issue Rivhit Receipts From Whatsapp Photos Using Google Vision and Gpt-4o

Issue Rivhit Receipts From Whatsapp Photos Using Google Vision and Gpt-4o

Byachiya @achiya on n8n.io

Courier sends an invoice photo to WhatsApp → AI extracts all details via Google Vision OCR Courier sends a payment photo (check, bank transfer, credit card voucher) → AI matches it to the invoice AI presents a summary and asks for confirmation Once approved — receipt is created…

Webhook trigger★★★★☆ complexityAI-powered23 nodesHTTP RequestAgentTool CodeMemory Buffer WindowOpenAI Chat@Devlikeapro/N8N Nodes WahaN8N Nodes Waha
AI & RAG Trigger: Webhook Nodes: 23 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → HTTP Request 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 →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "74c4a220-ac0d-4b39-8d76-b3af3fca983f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -768,
        -144
      ],
      "parameters": {
        "color": 5,
        "width": 420,
        "height": 620,
        "content": "## WhatsApp AI Receipt Agent\n**Automatically issue receipts from invoice & payment photos**\n\n### Who is it for\nSmall businesses using Rivhit accounting + WhatsApp for field payment collection.\n\n### How it works\n1. Courier sends an **invoice photo** \u2192 AI extracts all details via OCR\n2. Courier sends a **payment photo** (check / bank transfer / credit card voucher) \u2192 AI matches it to the invoice\n3. AI asks for **confirmation** \u2192 Courier approves\n4. **Receipt is created** in Rivhit, invoice is closed, PDF is sent back to WhatsApp\n\n### Supported payment types\nCash, checks, credit cards, bank transfers, and split (multiple) payments.\n\n### Setup\n1. Connect **WAHA** to your WhatsApp & set the webhook URL to this workflow\n2. Add your **Google Vision API key** to the HTTP Request node\n3. Add your **Rivhit API token** to the `api key` Set node\n4. Update the **Filter node** with your WhatsApp group ID\n5. Connect your **OpenAI credentials** to the Chat Model node\n6. **Activate** the workflow and start sending photos!"
      },
      "typeVersion": 1
    },
    {
      "id": "baffd3fe-4025-4478-bb38-267948a96e64",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        192
      ],
      "parameters": {
        "color": 2,
        "width": 520,
        "height": 280,
        "content": "### 1. Webhook & Filtering\nReceives incoming WhatsApp messages via WAHA and filters to only process messages from the designated bot group."
      },
      "typeVersion": 1
    },
    {
      "id": "fb257df8-fdd3-4684-a77c-ba16b28ca5f6",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        128
      ],
      "parameters": {
        "color": 2,
        "width": 560,
        "height": 280,
        "content": "### 2. OCR & Message Preparation\nIf the message contains an image \u2192 extract text using Google Vision OCR.\nThe extracted text is then appended to the courier's message so the AI Agent can read it."
      },
      "typeVersion": 1
    },
    {
      "id": "143cd48b-5a08-4b83-864f-9b22781f3e0d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        992,
        80
      ],
      "parameters": {
        "color": 2,
        "width": 620,
        "height": 320,
        "content": "### 3. AI Agent + Rivhit API Tool\nThe AI Agent reads conversation history, extracts invoice/payment details, and calls the Rivhit API tool to:\n- Issue receipts (cash, check, credit, transfer, multiple)\n- Close invoices\n- Search documents\n- Manage customers\n\nIt asks for confirmation before issuing any receipt."
      },
      "typeVersion": 1
    },
    {
      "id": "454ca326-846b-4e81-8ebc-5e1e3ef85658",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1760,
        80
      ],
      "parameters": {
        "color": 2,
        "width": 520,
        "height": 280,
        "content": "### 4. Response Delivery\nIf a document (PDF) was generated \u2192 send it as a file attachment with a caption.\nOtherwise \u2192 send a text-only reply."
      },
      "typeVersion": 1
    },
    {
      "id": "2208a531-5d3d-4a71-9912-efbf33102811",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        -48
      ],
      "parameters": {
        "color": 4,
        "width": 300,
        "height": 120,
        "content": "\u26a0\ufe0f **Add your Rivhit API token here**\nGet it from your Rivhit account settings \u2192 API section."
      },
      "typeVersion": 1
    },
    {
      "id": "5999ca01-3f84-417b-8082-e932e50b633c",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -128,
        480
      ],
      "parameters": {
        "color": 4,
        "width": 320,
        "height": 130,
        "content": "\u26a0\ufe0f **Replace with your WhatsApp group ID**\nFormat: `120363XXXXXXXXXX@g.us`\nYou can find it in the webhook payload under `payload.from`."
      },
      "typeVersion": 1
    },
    {
      "id": "cd821b62-0e6b-4cfd-85f6-39e4cebcca9f",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        0
      ],
      "parameters": {
        "color": 4,
        "width": 320,
        "height": 120,
        "content": "\u26a0\ufe0f **Add your Google Cloud Vision API key**\nEnable the Vision API in your Google Cloud Console and paste the key in the query parameter below."
      },
      "typeVersion": 1
    },
    {
      "id": "6adfcfac-aa2b-484b-8b06-90831c59f297",
      "name": "Is there a picture or not?",
      "type": "n8n-nodes-base.if",
      "position": [
        400,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4a0c648d-9128-41d6-916b-faf218d7f8cb",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Webhook').item.json.body.payload.hasMedia }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "7f718c14-a8c7-42a5-b1e7-191bae1c7890",
      "name": "Preparing a message",
      "type": "n8n-nodes-base.code",
      "position": [
        848,
        304
      ],
      "parameters": {
        "jsCode": "// Code node: \"Prepare Message\"\n// Place this after the Webhook and text extraction, before the AI Agent\nvar webhookBody = \"\";\ntry {\n  webhookBody = $('Webhook').item.json.body.payload.body || \"\";\n} catch(e) {}\nif (!webhookBody) webhookBody = \"I sent an image\";\nvar ocrText = \"\";\ntry {\n  ocrText = $('Extracting text from images').item.json.responses[0].textAnnotations[0].description || \"\";\n} catch(e) {}\nvar userMessage = webhookBody;\nif (ocrText) {\n  userMessage += \"\\n\\n[Text extracted from the image I sent:]\\n\" + ocrText;\n}\nreturn { json: { userMessage: userMessage } };"
      },
      "typeVersion": 2
    },
    {
      "id": "78348c53-84fb-4c8d-8691-07fbb768b2f0",
      "name": "Extracting text from images",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        624,
        208
      ],
      "parameters": {
        "url": "https://vision.googleapis.com/v1/images:annotate",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"requests\": [\n    {\n      \"image\": {\n        \"source\": {\n          \"imageUri\": \"{{ $('Webhook').item.json.body.payload.media.url }}\"\n        }\n      },\n      \"features\": [\n        {\n          \"type\": \"DOCUMENT_TEXT_DETECTION\"\n        }\n      ]\n    }\n  ]\n}",
        "sendBody": true,
        "sendQuery": true,
        "specifyBody": "json",
        "queryParameters": {
          "parameters": [
            {
              "name": "key"
            }
          ]
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "ae295294-5be0-426b-b91e-8da94903cb2b",
      "name": "api key",
      "type": "n8n-nodes-base.set",
      "position": [
        1072,
        304
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "4816008f-8c17-4571-8904-0b39a4d423d2",
              "name": "api_token",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "982f57fc-2d3b-4afe-ad8f-262489dff9da",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1360,
        304
      ],
      "parameters": {
        "text": "={{ $('Preparing a message').item.json.userMessage }}",
        "options": {
          "systemMessage": "=You are a smart receipt agent that works with couriers on WhatsApp. You receive images of invoices and payment methods, issue receipts in Rivhit, close invoices, and send everything back.\n\n\u2550\u2550\u2550 Supreme Rules \u2550\u2550\u2550\n\nRule 1 - Images: When the courier sends an image, the extracted text is attached to their message (after \"[Text extracted from the image I sent:]\"). Analyze that text immediately. Never say you can't see an image or ask them to resend - if there's attached text, that's the image content.\n\nRule 2 - Conversation History: Read all previous messages before responding. Everything mentioned earlier (invoice number, customer name, amount, tax ID) is still valid. Never ask for information that was already provided! When a payment arrives after an invoice - the payment belongs to that invoice. Don't ask \"which invoice\" or \"send the invoice number\" if it was already mentioned.\n\nRule 3 - Style: You're the courier's buddy on WhatsApp. Write warm, short, and natural. No formal phrasing. Say \"Alright, issuing a receipt!\" not \"I will now proceed with the receipt issuance process\". Say \"Got it!\" not \"Acknowledgment received successfully\". No technical words like \"OCR\", \"customer number\", \"JSON\", \"API\", \"customer_id\". When presenting extracted details (invoice, check, etc.) - separate lines for readability! Each detail on its own line, not everything crammed into one paragraph.\n\nRule 4 - Don't Ask, Do: If something fails (like closing an invoice) - don't ask the courier what to do. Try again, check details (Document.Details), fix it yourself. The courier doesn't understand technology. Only if it truly can't be resolved - briefly report there's an issue and you'll handle it.\n\nRule 5 - Approval Before Issuance (Two Steps!):\nStep A - Request approval: Present a summary (amount, customer name, payment type) and ask \"Issue it?\". \u26a0\ufe0f Do NOT call any tools! Do NOT perform any create action! Only send a message with message only (no document_url, no document_name).\nStep B - Only after approval: When the courier responds \"yes\" / \"approved\" / \"let's go\" in the next message - only then call the tool and issue the document.\nClosing an invoice after a receipt does not require separate approval - it happens automatically.\n\nRule 6 - Invoice First, Receipt After: Never issue a receipt before receiving an invoice! The flow is always: Invoice \u2192 Payment \u2192 Approval \u2192 Receipt. If the courier sends a payment without an invoice being sent first - ask them to send the invoice first. The customer name on the receipt is always taken from the invoice (last_name).\n\nRule 7 - Maximum Detail Filling: When creating a document, every detail must go into its designated field - not into comments!\n\n\u26a0\ufe0f last_name is always mandatory! This is what appears in the \"To:\" field on the receipt. Take the name from the invoice and send it as last_name. Without it, the receipt comes out with an empty \"To:\" field!\n\nFirst fill in the correct fields:\n- last_name: Customer/company name from the invoice (mandatory!)\n- id_number: Tax ID/ID number from the invoice\n- phone: Phone from the invoice\n- email: Email from the invoice\n- document_number: Invoice number (appears as reference)\n- bank_code, branch_number, bank_account_number: Bank details (for check/transfer)\n- check_number: Check number\n- due_date: Maturity date from the check (not today's date! Use the date printed on the check)\n- reference_number: Reference number from bank transfer (will be added to comments automatically)\n- card_number: Last 4 digits of credit card\n- voucher_number: Credit card voucher number\nOnly after filling all fields, add to comments things that don't have a dedicated field (e.g.: against which invoices, payer name if different from customer, additional details)\n\n\u2550\u2550\u2550 Bank Codes \u2550\u2550\u2550\n\nMust send the correct code! The number printed on the check is the bank code:\n4 - Yahav\n9 - Postal Bank\n10 - Leumi\n11 - Discount\n12 - Hapoalim\n13 - Igud (merged into Discount)\n14 - Otsar Ha-Hayal\n17 - Mercantile (part of Discount)\n20 - Mizrahi Tefahot\n31 - International (Beinleumi)\n46 - Massad\n54 - Jerusalem\n\nNote: Mizrahi Tefahot = 20 (not 31!). Mercantile = 17.\n\n\u2550\u2550\u2550 Immediate Documentation \u2550\u2550\u2550\n\nEvery time you analyze an image, you must mention all extracted details in your message. The image data is not saved automatically - only what you write in the message is saved!\n\nInvoice: Number, amount, customer name, customer number (if available), tax ID/ID number (if available), date\nCheck: Check number, bank, branch, account, maturity date (the date written on the check, not today's date!). Check amount = invoice amount (don't ask what the amount is! Automatically record the invoice amount. Only ask about the amount if there are multiple payment methods and you need to know the split).\nMercantile / Discount bank: There are two account numbers (short and long). Always use the account number written at the bottom of the check, ignore the short number that appears at the top right.\nIf unsure about a specific detail (check number, maturity date, branch, etc.) - suggest several numbered options that seem likely and ask the courier to choose, for example: \"Not sure about the check number, which is correct? 1 - 5003951, 2 - 5003961, 3 - Something else\"\nCredit card voucher: Last 4 digits, card type, amount, installments\nBank transfer: Reference number, amount, date, payer's bank details (if shown)\n\n\u2550\u2550\u2550 Real Data Only \u2550\u2550\u2550\n\nUse only data from images or the conversation. Never make up numbers or amounts. Missing information? Ask the courier.\n\n\u2550\u2550\u2550 No Partial Amounts \u2550\u2550\u2550\n\nBefore issuing a receipt, verify that payment total = invoice amount. If partial - tell the courier how much is missing.\n\n\u2550\u2550\u2550 Closing Invoice After Receipt \u2550\u2550\u2550\n\nAfter a successful receipt linked to an invoice (closes automatically, no need to ask for additional approval):\n1. Create receipt \u2192 get receipt number\n2. Immediately close_invoice with invoice number, receipt number, today's date (DD/MM/YYYY), amount\n3. Report to courier\n\nImportant regarding invoice numbers: An invoice number in format \"01/003619\" consists of document type (01) and number (003619). Send the full number as-is (the code will know how to parse it). If close_invoice fails - don't ask the courier for the invoice number, because it was already mentioned earlier. Try again with the number from the conversation.\n\n\u2550\u2550\u2550 Customer Identification \u2550\u2550\u2550\n\nWhen creating a receipt or document, you must fill in as many details as possible:\n\nCustomer identification (by priority order):\n1. Customer number exists on invoice \u2192 use it as customer_id\n2. No customer number but tax ID/ID exists \u2192 send customer_id: 0, find_by_id: true, id_number: \"the number\"\n3. No identifier at all \u2192 send customer_id: 0, create_customer: true, last_name from invoice\n\n\u2550\u2550\u2550 Workflow \u2550\u2550\u2550\n\nStep 1 - Invoice (mandatory!): The courier must send an invoice first. List all details (number, amount, customer name, tax ID) and ask how they paid. If the courier sends a payment without an invoice - ask them to send the invoice first.\nStep 2 - Payment: The payment belongs to the invoice mentioned earlier in the conversation (don't ask which invoice!). List payment details.\nStep 3 - Approval: Before issuing the receipt, present a summary: \"Issuing a receipt for [amount] \u20aa to [customer name from invoice], payment by [type]. Issue it?\" and wait for explicit approval.\nStep 4 - Receipt: Only after approval! create_receipt_* \u2192 close_invoice \u2192 send link.\nStep 5 - Split payment (courier sends \"1\"): Collect everything \u2192 request approval \u2192 create_receipt_multiple \u2192 close_invoice.\n\nAt the end of every message waiting for payment, add:\n1 - Multiple payment methods\nCancel - Cancel\n\n\u2550\u2550\u2550 Additional Scenarios \u2550\u2550\u2550\n\nCancel receipt: cancel_receipt \u2192 if linked to invoice also reopen_document \u2192 report\nCancel document: cancel_document \u2192 report\nSend by email: send_receipt or send_document \u2192 report\nDocument details: get_receipt_details or get_document_details \u2192 display\nCustomer balance: get_balance \u2192 display\nSearch: search_receipt or search_invoice \u2192 display\nUpdate customer: update_customer \u2192 report\nNew customer: create_customer \u2192 report\nTax invoice receipt: create_invoice_receipt_extended\nCredit note: create_credit_note \u2192 report\nDeferred receipt: create_receipt_refund \u2192 report\n\n\u2550\u2550\u2550 Response Format \u2550\u2550\u2550\n\nWhen requesting approval (before issuance - do NOT call any tools!):\n{\n  \"message\": \"Summary + Issue it?\"\n}\n\u26a0\ufe0f No document_url, no document_name! Only message!\n\nWhen issuing a document (only after courier approved!):\n{\n  \"message\": \"Message to courier - warm and natural. Mention if the receipt was sent to the customer by email or not.\",\n  \"document_url\": \"link to document\",\n  \"document_name\": \"receipt_12345.pdf\"\n}\n\ndocument_name: \"receipt_[number].pdf\" or \"invoice_[number].pdf\" depending on document type.\n\nWhen not issuing a document:\n{\n  \"message\": \"Message to courier\"\n}\n\nImportant: If you sent an email in the create_receipt parameters (and no error) - mention in the message that the receipt was sent to the customer by email. If there was no email - mention that the receipt was not sent by email.\n\n\u2550\u2550\u2550 Examples \u2550\u2550\u2550\n\nReceiving an invoice:\n{\n  \"message\": \"Got it!\\n\\nInvoice 1234\\nCustomer: Ofek Company\\nTax ID: 515188092\\nAmount: 2,500 \u20aa\\nDate: 07/02/2026\\n\\nHow did they pay? Send a photo of the payment or let me know if it's cash.\\n\\n1 - Multiple payment methods\\nCancel - Cancel\"\n}\n\nReceiving a bank transfer (invoice already known from conversation - don't ask again!):\n{\n  \"message\": \"Great! Got the transfer confirmation\\n\\nAmount: 2,500 \u20aa\\nReference: 41023\\nDate: 07/02/2026\\n\\nAmount matches invoice 1234 for Ofek Company. Issuing a receipt for 2,500 \u20aa to Ofek Company, bank transfer. Issue it?\"\n}\n\nCheck with maturity date:\n{\n  \"message\": \"Got the check!\\n\\nNumber: 7890\\nBank: Hapoalim (12)\\nBranch: 456\\nAccount: 789012\\nAmount: 2,500 \u20aa\\nMaturity date: 15/03/2026\\n\\nDetails correct? Once you confirm I'll issue the receipt.\"\n}\n\nCheck without maturity date:\n{\n  \"message\": \"Got the check!\\n\\nNumber: 7890\\nBank: Hapoalim (12)\\nBranch: 456\\nAccount: 789012\\nAmount: 2,500 \u20aa\\n\\nCouldn't read the maturity date - what does it say?\"\n}\n\nIssuing a receipt (with email):\n{\n  \"message\": \"All done! Receipt 5678 created and invoice closed. Receipt was sent to the customer by email.\",\n  \"document_url\": \"https://...\",\n  \"document_name\": \"receipt_5678.pdf\"\n}\n\nIssuing a receipt (without email):\n{\n  \"message\": \"All done! Receipt 5678 created and invoice closed. Note - there was no customer email so the receipt wasn't sent to them.\",\n  \"document_url\": \"https://...\",\n  \"document_name\": \"receipt_5678.pdf\"\n}\n\nPartial amount:\n{\n  \"message\": \"Got a check for 1,500 \u20aa, but the invoice is for 2,500 \u20aa so there's still 1,000 \u20aa missing. Send me the payment for the balance.\\n\\n1 - Multiple payment methods\\nCancel - Cancel\"\n}\n\nCash (approval request):\n{\n  \"message\": \"Got it, 2,500 \u20aa cash. Issuing a receipt for 2,500 \u20aa to Ofek Company, cash. Issue it?\"\n}\n\nCourier approval (after courier responded \"yes\" / \"approved\" / \"let's go\"):\n{\n  \"message\": \"All done! Receipt 5679 created and invoice closed. Receipt was sent to the customer by email.\",\n  \"document_url\": \"https://...\",\n  \"document_name\": \"receipt_5679.pdf\"\n}\n\nCancel receipt:\n{\n  \"message\": \"Done! Receipt 5678 cancelled and invoice 1234 reopened.\"\n}"
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "dd4267cf-848b-4aac-bf57-91a729cca290",
      "name": "rivhit_api",
      "type": "@n8n/n8n-nodes-langchain.toolCode",
      "position": [
        1552,
        528
      ],
      "parameters": {
        "jsCode": "var rawData = $input.item.json;\n// n8n AI Agent sends data as some_input (JSON string) - needs parsing\nvar parsed = null;\nif (rawData.some_input) {\n  try { parsed = JSON.parse(rawData.some_input); } catch(e) {}\n}\nvar API_TOKEN = rawData.api_token || (parsed && parsed.api_token) || \"\";\nvar action = (parsed && parsed.action) || rawData.action;\nvar params = (parsed && parsed.params) || rawData.params || {};\nvar BASE_URL = \"https://api.rivhit.co.il/online/RivhitOnlineAPI.svc\";\n\nvar url = \"\";\nvar body = {};\n\n// Parse document number: \"01/003619\" \u2192 3619, \" 3619\" \u2192 3619\nfunction parseDocNum(val) {\n  if (val === undefined || val === null) return val;\n  var s = String(val);\n  if (s.indexOf(\"/\") !== -1) s = s.split(\"/\").pop();\n  var n = parseInt(s);\n  return isNaN(n) ? val : n;\n}\n\n// Convert value to integer (remove non-numeric characters like dashes)\nfunction toInt(val) {\n  if (val === undefined || val === null) return val;\n  var n = parseInt(String(val).replace(/\\D/g, ''));\n  return isNaN(n) ? val : n;\n}\n\n// Convert date: \"08022026\" \u2192 \"08/02/2026\", already correct format? return as-is\nfunction formatDate(val) {\n  if (!val) return val;\n  var s = String(val);\n  if (s.indexOf(\"/\") !== -1 || s.indexOf(\"-\") !== -1) return s;\n  if (s.length === 8) return s.substring(0,2) + \"/\" + s.substring(2,4) + \"/\" + s.substring(4);\n  return s;\n}\n\n// Hide api_token from body before printing in errors\nfunction safeBody(b) {\n  var copy = {};\n  for (var k in b) {\n    if (k === \"api_token\") {\n      copy[k] = \"***\";\n    } else {\n      copy[k] = b[k];\n    }\n  }\n  return JSON.stringify(copy);\n}\n\n// Build payment object for bank transfer\nfunction buildTransferPayment(p) {\n  var pay = { payment_type: 9, amount_nis: p.amount };\n  if (p.bank_code) pay.bank_code = toInt(p.bank_code);\n  if (p.branch_number) pay.branch_number = toInt(p.branch_number);\n  if (p.bank_account_number) pay.bank_account_number = toInt(p.bank_account_number);\n  if (p.due_date) pay.due_date = formatDate(p.due_date);\n  return pay;\n}\n\n// Build payment object for check\nfunction buildCheckPayment(p) {\n  var pay = {\n    payment_type: 1,\n    amount_nis: p.amount,\n    bank_code: toInt(p.bank_code),\n    branch_number: toInt(p.branch_number),\n    bank_account_number: toInt(p.bank_account_number),\n    check_number: toInt(p.check_number)\n  };\n  if (p.due_date) pay.due_date = formatDate(p.due_date);\n  return pay;\n}\n\n// Build payment object for credit card\nfunction buildCreditPayment(p) {\n  var pay = { payment_type: 4, amount_nis: p.amount };\n  if (p.number_of_payments) pay.number_of_payments = toInt(p.number_of_payments);\n  if (p.credit_type) pay.credit_type = toInt(p.credit_type);\n  if (p.card_number) pay.card_number = String(p.card_number);\n  if (p.voucher_number) pay.voucher_number = String(p.voucher_number);\n  return pay;\n}\n\n// Check credit card detail completeness - returns warning if fields are missing\nfunction creditWarning(p) {\n  var missing = [];\n  if (!p.card_number) missing.push(\"card_number (last 4 digits)\");\n  if (!p.voucher_number) missing.push(\"voucher_number (reference/voucher)\");\n  if (missing.length > 0) return \" | \u26a0\ufe0f Missing in receipt: \" + missing.join(\", \") + \" - ask the customer!\";\n  return \"\";\n}\n\n// === Receipts ===\n\nif (action === \"create_receipt_cash\") {\n  url = BASE_URL + \"/Receipt.New\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: 1,\n    customer_id: params.customer_id || 0,\n    payments: [{ payment_type: 2, amount_nis: params.amount }]\n  };\n\n} else if (action === \"create_receipt_check\") {\n  url = BASE_URL + \"/Receipt.New\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: 1,\n    customer_id: params.customer_id || 0,\n    payments: [buildCheckPayment(params)]\n  };\n\n} else if (action === \"create_receipt_credit\") {\n  url = BASE_URL + \"/Receipt.New\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: 1,\n    customer_id: params.customer_id || 0,\n    payments: [buildCreditPayment(params)]\n  };\n\n} else if (action === \"create_receipt_transfer\") {\n  url = BASE_URL + \"/Receipt.New\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: 1,\n    customer_id: params.customer_id || 0,\n    payments: [buildTransferPayment(params)]\n  };\n\n} else if (action === \"create_receipt_multiple\") {\n  url = BASE_URL + \"/Receipt.New\";\n  var payArr = [];\n  for (var i = 0; i < params.payments.length; i++) {\n    var p = params.payments[i];\n    if (p.type === \"cash\") {\n      payArr.push({ payment_type: 2, amount_nis: p.amount });\n    } else if (p.type === \"check\") {\n      payArr.push(buildCheckPayment(p));\n    } else if (p.type === \"credit\") {\n      payArr.push(buildCreditPayment(p));\n    } else if (p.type === \"transfer\") {\n      payArr.push(buildTransferPayment(p));\n    }\n  }\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: 1,\n    customer_id: params.customer_id || 0,\n    payments: payArr\n  };\n\n} else if (action === \"create_receipt_refund\") {\n  url = BASE_URL + \"/Receipt.New\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: 3,\n    customer_id: params.customer_id || 0,\n    payments: [{ payment_type: 2, amount_nis: params.amount }]\n  };\n\n// === Closing/Cancelling Documents ===\n\n} else if (action === \"close_invoice\") {\n  url = BASE_URL + \"/Document.Close\";\n  body = {\n    api_token: API_TOKEN,\n    document_type: params.document_type || 1,\n    document_number: parseDocNum(params.document_number),\n    closing_type: params.closing_type || 1,\n    closing_number: parseDocNum(params.closing_number),\n    closing_date: formatDate(params.closing_date),\n    amount_close: params.amount_close,\n    document_is_receipt: false\n  };\n\n} else if (action === \"reopen_document\") {\n  url = BASE_URL + \"/Document.Reopen\";\n  body = {\n    api_token: API_TOKEN,\n    document_type: params.document_type || 1,\n    document_number: parseDocNum(params.document_number)\n  };\n\n} else if (action === \"cancel_receipt\") {\n  url = BASE_URL + \"/Receipt.Cancel\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: params.receipt_type || 1,\n    receipt_number: params.receipt_number\n  };\n  if (params.comments) body.comments = params.comments;\n  if (params.email) body.email_to = params.email;\n  if (params.cancellation_date) body.cancellation_date = formatDate(params.cancellation_date);\n\n} else if (action === \"cancel_document\") {\n  url = BASE_URL + \"/Document.Cancel\";\n  body = {\n    api_token: API_TOKEN,\n    document_type: params.document_type || 1,\n    document_number: parseDocNum(params.document_number)\n  };\n  if (params.comments) body.comments = params.comments;\n  if (params.email) body.email_to = params.email;\n  if (params.cancellation_date) body.cancellation_date = formatDate(params.cancellation_date);\n\n// === Search & Document Details ===\n\n} else if (action === \"search_receipt\") {\n  url = BASE_URL + \"/Receipt.List\";\n  body = { api_token: API_TOKEN };\n  if (params.from_date) body.from_date = params.from_date;\n  if (params.to_date) body.to_date = params.to_date;\n  if (params.customer_id) {\n    body.from_customer_id = params.customer_id;\n    body.to_customer_id = params.customer_id;\n  }\n\n} else if (action === \"get_receipt_details\") {\n  url = BASE_URL + \"/Receipt.Details\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: params.receipt_type || 1,\n    receipt_number: params.receipt_number\n  };\n\n} else if (action === \"search_invoice\") {\n  url = BASE_URL + \"/Document.List\";\n  body = {\n    api_token: API_TOKEN,\n    from_document_type: 1,\n    to_document_type: 1,\n    from_date: params.from_date,\n    to_date: params.to_date\n  };\n  if (params.customer_id) {\n    body.from_customer_id = params.customer_id;\n    body.to_customer_id = params.customer_id;\n  }\n\n} else if (action === \"get_document_details\") {\n  url = BASE_URL + \"/Document.Details\";\n  body = {\n    api_token: API_TOKEN,\n    document_type: params.document_type || 1,\n    document_number: parseDocNum(params.document_number)\n  };\n\n// === Sending Documents by Email ===\n\n} else if (action === \"send_receipt\") {\n  url = BASE_URL + \"/Receipt.Send\";\n  body = {\n    api_token: API_TOKEN,\n    receipt_type: params.receipt_type || 1,\n    receipt_number: params.receipt_number,\n    email: params.email\n  };\n\n} else if (action === \"send_document\") {\n  url = BASE_URL + \"/Document.Send\";\n  body = {\n    api_token: API_TOKEN,\n    document_type: params.document_type || 1,\n    document_number: parseDocNum(params.document_number),\n    email: params.email\n  };\n\n// === Documents ===\n\n} else if (action === \"create_invoice_receipt\") {\n  url = BASE_URL + \"/Document.New\";\n  body = {\n    api_token: API_TOKEN,\n    document_type: 2,\n    customer_id: params.customer_id || 0,\n    price_include_vat: true,\n    items: params.items,\n    payments: params.payments\n  };\n  if (params.comments) body.comments = params.comments;\n\n} else if (action === \"create_invoice_receipt_extended\") {\n  url = BASE_URL + \"/Document.NewExtended\";\n  body = {\n    api_token: API_TOKEN,\n    document_type: params.document_type || 2,\n    receipt_type: params.receipt_type || 1,\n    customer_id: params.customer_id || 0,\n    price_include_vat: true,\n    items: params.items,\n    payments: params.payments\n  };\n  if (params.comments) body.comments = params.comments;\n\n} else if (action === \"create_credit_note\") {\n  url = BASE_URL + \"/Document.New\";\n  var creditItems = [];\n  for (var ci = 0; ci < params.items.length; ci++) {\n    var item = {};\n    for (var ck in params.items[ci]) item[ck] = params.items[ci][ck];\n    if (item.price_nis > 0) item.price_nis = -item.price_nis;\n    creditItems.push(item);\n  }\n  body = {\n    api_token: API_TOKEN,\n    document_type: 3,\n    customer_id: params.customer_id || 0,\n    price_include_vat: true,\n    items: creditItems\n  };\n  if (params.original_document_number) {\n    body.closed_document_type = params.original_document_type || 1;\n    body.closed_document_number = parseDocNum(params.original_document_number);\n  }\n  if (params.comments) body.comments = params.comments;\n\n// === Customers ===\n\n} else if (action === \"get_customer\") {\n  url = BASE_URL + \"/Customer.Get\";\n  body = { api_token: API_TOKEN };\n  if (params.customer_id) body.customer_id = params.customer_id;\n  if (params.email) body.email = params.email;\n  if (params.acc_ref) body.acc_ref = params.acc_ref;\n\n} else if (action === \"get_balance\") {\n  url = BASE_URL + \"/Customer.Balance\";\n  body = {\n    api_token: API_TOKEN,\n    customer_id: params.customer_id\n  };\n\n} else if (action === \"search_customer\") {\n  url = BASE_URL + \"/Customer.List\";\n  body = { api_token: API_TOKEN };\n  if (params.customer_type) body.customer_type = params.customer_type;\n\n} else if (action === \"create_customer\") {\n  url = BASE_URL + \"/Customer.New\";\n  body = {\n    api_token: API_TOKEN,\n    last_name: params.last_name\n  };\n  if (params.first_name) body.first_name = params.first_name;\n  if (params.phone) body.phone = params.phone;\n  if (params.email) body.email = params.email;\n  if (params.address) body.address = params.address;\n  if (params.city) body.city = params.city;\n  if (params.id_number) body.id_number = params.id_number;\n  if (params.vat_number) body.vat_number = params.vat_number;\n\n} else if (action === \"update_customer\") {\n  url = BASE_URL + \"/Customer.Update\";\n  body = {\n    api_token: API_TOKEN,\n    customer_id: params.customer_id\n  };\n  if (params.last_name) body.last_name = params.last_name;\n  if (params.first_name) body.first_name = params.first_name;\n  if (params.phone) body.phone = params.phone;\n  if (params.email) body.email = params.email;\n  if (params.address) body.address = params.address;\n  if (params.city) body.city = params.city;\n  if (params.zipcode) body.zipcode = params.zipcode;\n\n// === General Information ===\n\n} else if (action === \"get_banks\") {\n  url = BASE_URL + \"/Payment.BankList\";\n  body = { api_token: API_TOKEN };\n\n} else if (action === \"get_vat_rate\") {\n  url = BASE_URL + \"/Accounting.VatRate\";\n  body = { api_token: API_TOKEN };\n\n} else {\n  return \"Unrecognized action: \" + action + \" | raw keys: \" + Object.keys(rawData).join(\",\") + \" | some_input exists: \" + !!rawData.some_input;\n}\n\n// Add comments from agent (for all receipt and document types)\nif (params.comments && action.indexOf(\"create_receipt\") === 0 && !body.comments) {\n  body.comments = params.comments;\n}\n\n// Add bank transfer reference_number to comments\nif (params.reference_number && action.indexOf(\"create_receipt\") === 0) {\n  var refComment = \"Transfer reference: \" + params.reference_number;\n  body.comments = body.comments ? (body.comments + \" | \" + refComment) : refComment;\n}\n\n// Add reference and automatic comment with invoice number\nif (params.document_number && (action.indexOf(\"create_receipt\") === 0)) {\n  var refNum = parseDocNum(params.document_number);\n  if (typeof refNum === \"number\") body.reference = refNum;\n  var autoComment = \"Against tax invoice number \" + params.document_number;\n  body.comments = body.comments ? (body.comments + \" | \" + autoComment) : autoComment;\n}\n\n// Automatic customer lookup by tax ID or email\nif (action.indexOf(\"create_receipt\") === 0 || action.indexOf(\"create_invoice\") === 0 || action === \"create_credit_note\") {\n  if (params.find_by_id) {\n    body.find_by_id = true;\n    body.create_customer = true;\n  }\n  if (params.find_by_mail) body.find_by_mail = true;\n  if (params.create_customer) body.create_customer = true;\n  var idVal = params.id_number || params.vat_number;\n  if (idVal) {\n    body.id_number = idVal;\n    body.vat_number = idVal;\n  }\n  if (params.last_name) body.last_name = params.last_name;\n  if (params.first_name) body.first_name = params.first_name;\n  if (params.phone) body.phone = params.phone;\n  if (params.email) {\n    body.email_to = params.email;\n    body.send_mail = true;\n  }\n}\n\ntry {\n  var result = await this.helpers.httpRequest({\n    method: \"POST\",\n    url: url,\n    headers: {\n      \"Content-Type\": \"application/json\",\n      \"Accept\": \"application/json\"\n    },\n    body: body,\n    json: true\n  });\n  var resultStr = JSON.stringify(result);\n  if (action === \"create_receipt_credit\") {\n    resultStr += creditWarning(params);\n  }\n  if (action === \"create_receipt_multiple\" && params.payments) {\n    for (var wi = 0; wi < params.payments.length; wi++) {\n      if (params.payments[wi].type === \"credit\") {\n        resultStr += creditWarning(params.payments[wi]);\n        break;\n      }\n    }\n  }\n  return resultStr;\n} catch (e) {\n  var errInfo = \"API Error: \" + e.message;\n  errInfo += \" | sent body: \" + safeBody(body);\n  try {\n    if (e.cause && e.cause.response && e.cause.response.body) {\n      errInfo += \" | server response: \" + JSON.stringify(e.cause.response.body);\n    } else if (e.response && e.response.body) {\n      errInfo += \" | server response: \" + JSON.stringify(e.response.body);\n    } else if (e.body) {\n      errInfo += \" | server response: \" + JSON.stringify(e.body);\n    } else if (e.data) {\n      errInfo += \" | server response: \" + JSON.stringify(e.data);\n    }\n  } catch(x) {}\n  try { errInfo += \" | error keys: \" + Object.keys(e).join(\",\"); } catch(x) {}\n  return errInfo;\n}",
        "schemaType": "manual",
        "description": "=Rivhit API calls. The token is set automatically - do not send it.\nSend JSON with action and params only.\n\u26a0\ufe0f Always use real data from the conversation - not values from the examples!\n\u26a0\ufe0f You must send every detail you have - do not omit any information!\n\n\u2550\u2550\u2550 Receipts \u2550\u2550\u2550\n\nCash receipt:\n{\"action\": \"create_receipt_cash\", \"params\": {\"customer_id\": ..., \"document_number\": ..., \"amount\": ...}}\nOptional: comments\n\nCheck receipt:\n{\"action\": \"create_receipt_check\", \"params\": {\"customer_id\": ..., \"document_number\": ..., \"amount\": ..., \"bank_code\": ..., \"branch_number\": ..., \"bank_account_number\": ..., \"check_number\": ...}}\nOptional: due_date (DD/MM/YYYY, maturity date from the check), comments\n\nCredit card receipt:\n{\"action\": \"create_receipt_credit\", \"params\": {\"customer_id\": ..., \"document_number\": ..., \"amount\": ..., \"card_number\": \"1234\", \"voucher_number\": \"A123456\"}}\n\u26a0\ufe0f Mandatory for credit card transactions! card_number = last 4 digits of card, voucher_number = reference/voucher number from the clearing\nOptional: number_of_payments (installment count), credit_type (1=regular, 2=installments, 3=credit), comments\n\nBank transfer receipt:\n{\"action\": \"create_receipt_transfer\", \"params\": {\"customer_id\": ..., \"document_number\": ..., \"amount\": ...}}\nOptional: bank_code, branch_number, bank_account_number, reference_number (reference number - will be added to comments automatically), due_date (DD/MM/YYYY), comments\n\nMultiple payment receipt:\n{\"action\": \"create_receipt_multiple\", \"params\": {\"customer_id\": ..., \"document_number\": ..., \"payments\": [{\"type\": \"cash|check|credit|transfer\", \"amount\": ..., ...additional fields by type}]}}\nFor each payment add the relevant fields by type:\n- credit: card_number (last 4 digits), voucher_number (reference), number_of_payments, credit_type\n- check: bank_code, branch_number, bank_account_number, check_number, due_date\n- transfer: bank_code, branch_number, bank_account_number, reference_number, due_date\nOptional: comments\n\nRefund receipt (cancellation/credit):\n{\"action\": \"create_receipt_refund\", \"params\": {\"customer_id\": ..., \"amount\": ..., \"comments\": \"reason for cancellation\"}}\n\n\u2550\u2550\u2550 Automatic Customer Lookup \u2550\u2550\u2550\n\nIn any create_receipt, create_invoice, or create_credit_note action, you can add:\n- find_by_id: true + id_number \u2192 automatically searches for customer by ID/tax number (no need for Customer.Get!)\n- find_by_mail: true + email \u2192 searches for customer by email\n- create_customer: true + last_name \u2192 creates a new customer if not found\n- email \u2192 sends the document by email automatically\n\nWhen customer_id is unknown, send customer_id: 0 with find_by_id: true and the id_number.\n\nImportant! Always send every detail you have:\n- last_name, phone, email, id_number - customer details\n- document_number - invoice number (reference + automatic note)\n- comments - additional notes (only things that don't have a dedicated field)\n- bank_code, branch_number, bank_account_number - bank details (for check/transfer)\n- reference_number - reference number from bank transfer\n- check_number - check number\n\u26a0\ufe0f For credit card transactions - mandatory:\n- card_number - last 4 digits of credit card (mandatory!)\n- voucher_number - reference/voucher number from clearing (mandatory!)\n- number_of_payments - installment count (if applicable)\n- credit_type - 1=regular, 2=installments, 3=credit (if known)\n\n\u2550\u2550\u2550 Closing/Cancelling Documents \u2550\u2550\u2550\n\nClose invoice against receipt (mandatory after creating a receipt!):\n{\"action\": \"close_invoice\", \"params\": {\"document_type\": 1, \"document_number\": ..., \"closing_type\": 1, \"closing_number\": ..., \"closing_date\": \"DD/MM/YYYY\", \"amount_close\": ...}}\ndocument_number = the invoice number being closed\nclosing_number = the receipt number that was created\nclosing_date = closing date in DD/MM/YYYY format\n\nReopen a closed document:\n{\"action\": \"reopen_document\", \"params\": {\"document_type\": 1, \"document_number\": ...}}\n\nCancel receipt:\n{\"action\": \"cancel_receipt\", \"params\": {\"receipt_number\": ...}}\nOptional: receipt_type (default 1), comments, email, cancellation_date\n\nCancel document (invoice):\n{\"action\": \"cancel_document\", \"params\": {\"document_type\": 1, \"document_number\": ...}}\nOptional: comments, email, cancellation_date\n\n\u2550\u2550\u2550 Search & Document Details \u2550\u2550\u2550\n\nSearch invoice by date:\n{\"action\": \"search_invoice\", \"params\": {\"from_date\": \"DD/MM/YYYY\", \"to_date\": \"DD/MM/YYYY\"}}\nOptional: customer_id (filter by customer)\n\nSearch existing receipt:\n{\"action\": \"search_receipt\", \"params\": {\"from_date\": \"DD/MM/YYYY\", \"to_date\": \"DD/MM/YYYY\"}}\nOptional: customer_id\n\nFull receipt details:\n{\"action\": \"get_receipt_details\", \"params\": {\"receipt_number\": ...}}\nOptional: receipt_type (default 1)\n\nFull document details (invoice):\n{\"action\": \"get_document_details\", \"params\": {\"document_type\": 1, \"document_number\": ...}}\n\n\u2550\u2550\u2550 Sending Documents by Email \u2550\u2550\u2550\n\nSend existing receipt by email:\n{\"action\": \"send_receipt\", \"params\": {\"receipt_number\": ..., \"email\": \"...\"}}\n\nSend existing document by email:\n{\"action\": \"send_document\", \"params\": {\"document_type\": 1, \"document_number\": ..., \"email\": \"...\"}}\n\n\u2550\u2550\u2550 Customers \u2550\u2550\u2550\n\nGet customer details:\n{\"action\": \"get_customer\", \"params\": {\"customer_id\": ...}}\nAlso possible by: email\n\nCustomer balance:\n{\"action\": \"get_balance\", \"params\": {\"customer_id\": ...}}\n\nSearch customers:\n{\"action\": \"search_customer\", \"params\": {}}\n\nCreate new customer:\n{\"action\": \"create_customer\", \"params\": {\"last_name\": \"...\"}}\nOptional: first_name, phone, email, address, city, id_number, vat_number\n\nUpdate customer details:\n{\"action\": \"update_customer\", \"params\": {\"customer_id\": ..., \"phone\": \"...\", \"email\": \"...\"}}\n\n\u2550\u2550\u2550 Documents \u2550\u2550\u2550\n\nTax invoice receipt:\n{\"action\": \"create_invoice_receipt\", \"params\": {\"customer_id\": ..., \"items\": [{\"description\": \"...\", \"price_nis\": ..., \"quantity\": ...}], \"payments\": [{\"payment_type\": 2, \"amount_nis\": ...}]}}\n\nLinked invoice+receipt in one call:\n{\"action\": \"create_invoice_receipt_extended\", \"params\": {\"customer_id\": ..., \"items\": [{\"description\": \"...\", \"price_nis\": ..., \"quantity\": ...}], \"payments\": [{\"payment_type\": 2, \"amount_nis\": ...}]}}\nOptional: document_type (default 2), receipt_type (default 1), comments, email\n\nCredit note:\n{\"action\": \"create_credit_note\", \"params\": {\"customer_id\": ..., \"original_document_number\": ..., \"items\": [{\"description\": \"...\", \"price_nis\": ..., \"quantity\": ...}]}}\noriginal_document_number = the original invoice number against which the credit is issued (mandatory!)\nOptional: original_document_type (default 1 = tax invoice), comments\n\n\u2550\u2550\u2550 Information \u2550\u2550\u2550\n\nBank list:\n{\"action\": \"get_banks\", \"params\": {}}\n\nCurrent VAT rate:\n{\"action\": \"get_vat_rate\", \"params\": {}}",
        "specifyInputSchema": true
      },
      "typeVersion": 1.3
    },
    {
      "id": "28c6f496-39be-488a-98bf-97185298721d",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        1424,
        528
      ],
      "parameters": {
        "sessionKey": "={{ $('Webhook').item.json.body.payload._data.Info.SenderAlt }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 15
      },
      "typeVersion": 1.3
    },
    {
      "id": "a1297d66-e563-496b-b3e7-d611582f3a8e",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1296,
        528
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "gpt-4o"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "a913ad9e-69a0-4453-9c5a-61042c9e0a4a",
      "name": "Separation between the message and document link",
      "type": "n8n-nodes-base.code",
      "position": [
        1760,
        304
      ],
      "parameters": {
        "jsCode": "const output = $input.first().json.output;\nconst parsed = JSON.parse(output);\n\nreturn [{ json: parsed }];"
      },
      "typeVersion": 2
    },
    {
      "id": "51b97d72-221c-4fe3-8867-bb38cf10af45",
      "name": "Only if there is an attachment",
      "type": "n8n-nodes-base.if",
      "position": [
        1984,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "65ceb969-8536-4aa8-a9c4-42b4bdcfb6cf",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.document_url }}",
              "rightValue": "null"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "a7053452-9227-4724-9879-089c67109f6f",
      "name": "Send a file",
      "type": "@devlikeapro/n8n-nodes-waha.WAHA",
      "position": [
        2208,
        192
      ],
      "parameters": {
        "file": "={\n  \"mimetype\": \"application/pdf\",\n  \"filename\": \"{{ $json.document_name }}\",\n  \"url\": \"{{ $json.document_url }}\"\n} ",
        "chatId": "={{ $node[\"Webhook\"].json[\"body\"][\"payload\"][\"from\"] }}",
        "caption": "={{ $json.message }}",
        "session": "={{ $node[\"Webhook\"].json[\"body\"][\"session\"] }}",
        "reply_to": "={{ $node[\"Webhook\"].json[\"body\"][\"payload\"][\"_data\"][\"Info\"][\"ID\"] }}",
        "resource": "Chatting",
        "operation": "Send File",
        "requestOptions": {}
      },
      "typeVersion": 202502
    },
    {
      "id": "0eb297e6-28a3-41b6-af9b-539590195940",
      "name": "Send a text message",
      "type": "n8n-nodes-waha.WAHA",
      "position": [
        2208,
        400
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "={{ $node[\"Webhook\"].json[\"body\"][\"payload\"][\"from\"] }}",
        "session": "={{ $node[\"Webhook\"].json[\"body\"][\"session\"] }}",
        "reply_to": "={{ $node[\"Webhook\"].json[\"body\"][\"payload\"][\"_data\"][\"Info\"][\"ID\"] }}",
        "resource": "Chatting",
        "operation": "Send Text",
        "requestOptions": {}
      },
      "typeVersion": 202411
    },
    {
      "id": "fe6f8f46-95c6-4f7a-9f17-0690f0c0036d",
      "name": "Start Typing",
      "type": "@devlikeapro/n8n-nodes-waha.WAHA",
      "position": [
        176,
        304
      ],
      "parameters": {
        "chatId": "={{ $item(\"0\").$node[\"Only if it's from the bot's group\"].json[\"body\"][\"payload\"][\"from\"] }}",
        "session": "={{ $item(\"0\").$node[\"Only if it's from the bot's group\"].json[\"body\"][\"session\"] }}",
        "resource": "Chatting",
        "operation": "Start Typing",
        "requestOptions": {}
      },
      "typeVersion": 202502
    },
    {
      "id": "d71dd8e5-ea76-467b-a389-1846caed4716",
      "name": "Only if it's from the bot's group",
      "type": "n8n-nodes-base.filter",
      "position": [
        -48,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b7b997df-4a19-4f53-87af-b9d088614f2c",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.body.payload.from }}",
              "rightValue": "user@example.com"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "35dfe9c1-d8bd-4750-8803-c82070b0a5cd",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -272,
        304
      ],
      "parameters": {
        "path": "whatsapp-receipt-agent",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Only if it's from the bot's group",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "api key": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Separation between the message and document link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "rivhit_api": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Start Typing": {
      "main": [
        [
          {
            "node": "Is there a picture or not?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Preparing a message": {
      "main": [
        [
          {
            "node": "api key",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is there a picture or not?": {
      "main": [
        [
          {
            "node": "Extracting text from images",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Preparing a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extracting text from images": {
      "main": [
        [
          {
            "node": "Preparing a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only if there is an attachment": {
      "main": [
        [
          {
            "node": "Send a file",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send a text message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only if it's from the bot's group": {
      "main": [
        [
          {
            "node": "Start Typing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Separation between the message and document link": {
      "main": [
        [
          {
            "node": "Only if there is an attachment",
            "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

Courier sends an invoice photo to WhatsApp → AI extracts all details via Google Vision OCR Courier sends a payment photo (check, bank transfer, credit card voucher) → AI matches it to the invoice AI presents a summary and asks for confirmation Once approved — receipt is created…

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

Whatsapp Lead Agent. Uses httpRequest, hunter, @tavily/n8n-nodes-tavily, @mendable/n8n-nodes-firecrawl. Webhook trigger; 35 nodes.

HTTP Request, Hunter, @Tavily/N8N Nodes Tavily +11
AI & RAG

L&D_AgentsAI_ATIVO. Uses httpRequest, agent, googleCalendarTool, toolSerpApi. Webhook trigger; 93 nodes.

HTTP Request, Agent, Google Calendar Tool +9
AI & RAG

This n8n workflow orchestrates a powerful suite of AI Agents and automations to manage and optimize various aspects of an e-commerce operation, particularly for platforms like Shopify. It leverages La

Google Sheets, HTTP Request, Slack +10
AI & RAG

Lead Pipeline v3.0. Uses httpRequest, agent, lmChatAnthropic, toolThink. Webhook trigger; 77 nodes.

HTTP Request, Agent, Anthropic Chat +4
AI & RAG

My workflow 15. Uses httpRequest, memoryBufferWindow, agent, lmChatOpenAi. Webhook trigger; 74 nodes.

HTTP Request, Memory Buffer Window, Agent +5