{
  "name": "zettel-ocr",
  "nodes": [
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {
          "download": false,
          "userIds": "1542517546"
        }
      },
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        432
      ],
      "id": "795714e2-f221-49f8-be30-013f345c1a29",
      "name": "Telegram Trigger",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "file",
        "fileId": "={{ $json.message.photo[2].file_id }}",
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        224,
        432
      ],
      "id": "434f74a4-cd74-4fdc-a4cd-804ac2ff1903",
      "name": "Get a file",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "image",
        "operation": "analyze",
        "modelId": {
          "__rl": true,
          "value": "models/gemini-3-flash-preview",
          "mode": "list",
          "cachedResultName": "models/gemini-3-flash-preview"
        },
        "text": "Du bist ein hochpr\u00e4ziser Assistent f\u00fcr die Datenextraktion. Deine Aufgabe ist es, den angeh\u00e4ngten Kassenbon auszulesen.\n\nExtrahiere alle gekauften Artikel, deren Preise und ordne jeden Artikel einer passenden Kategorie zu.\nInterpretiere den internen Namen des Produktes in ein menschenlesbares Format.\n\nW\u00c4HLE F\u00dcR \"category\" AUSSCHLIESSLICH AUS DIESER LISTE:\n\nLebensmittel\n\nS\u00fc\u00dfwaren\n\nGetr\u00e4nke\n\nAlkohol\n\nDrogerie & Haushalt\n\nPfand/Leergut\n\nSonstiges\n\nWICHTIG F\u00dcR DIE AUSGABE:\n\nGib das Ergebnis AUSSCHLIESSLICH als valides JSON-Array aus.\n\nF\u00fcge absolut keinen einleitenden oder abschlie\u00dfenden Text hinzu.\n\nVerwende KEINE Markdown-Bl\u00f6cke (wie ```json). Beginne direkt mit der eckigen Klammer [ und ende mit ].\n\nDie Preise (\"cost\") m\u00fcssen als Zahlen (Floats) formatiert sein, nicht als Text (also 2.39 statt \"2.39\"). Beachte auch negative Werte bei R\u00fcckgaben/Leergut.\n\nVerwende exakt dieses Format als Vorlage f\u00fcr deine Ausgabe:\n[\n{\n\"item\": \"BE Junge Erbsen 1 kg\",\n\"interpretation\": \"Erbsen\",\n\"cost\": 2.39,\n\"category\": \"Lebensmittel\"\n},\n{\n\"item\": \"Frosch WM Aloe Vera 25WL\",\n\"interpretation\": \"Waschmittel\",\n\"cost\": 2.49,\n\"category\": \"Drogerie & Haushalt\"\n},\n{\n\"item\": \"JackDaniels ColaZero0,33L\",\n\"interpretation\": \"Jack Danies Cola\",\n\"cost\": 1.99,\n\"category\": \"Alkohol\"\n},\n{\n\"item\": \"Einwegpfand 19%\",\n\"interpretation\": \"Pfand,\n\"cost\": 0.25,\n\"category\": \"Pfand/Leergut\"\n}\n]",
        "inputType": "binary",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.1,
      "position": [
        448,
        432
      ],
      "id": "8eec2eeb-673e-4721-b7d4-ef1806c6aae1",
      "name": "Analyze an image",
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.content.parts[0].text }}",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "Stelle sicher, dass der Input dem angeforderten Schema entspricht."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        704,
        432
      ],
      "id": "5faee37a-9736-4bcf-95ad-bf467e453105",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"array\",\n  \"items\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"item\": {\n        \"type\": \"string\",\n        \"description\": \"Der Name des gekauften Artikels\"\n      },\n      \"interpretation\": {\n        \"type\": \"string\",\n        \"description\": \"Menschenlesbare Interpretation des Produktnames\"\n      },\n      \"cost\": {\n        \"type\": \"number\",\n        \"description\": \"Der Preis des Artikels als Zahl\"\n      },\n      \"category\": {\n  \"type\": \"string\",\n  \"description\": \"Die Kategorie des Artikels\"\n}\n    },\n    \"required\": [\"item\", \"cost\", \"category\"]\n  }\n}",
        "autoFix": true
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        800,
        656
      ],
      "id": "a6d33de3-ba68-4e66-909f-371b535b7bad",
      "name": "Structured Output Parser"
    },
    {
      "parameters": {
        "model": "anthropic/claude-haiku-4.5",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        880,
        864
      ],
      "id": "68cbe6fc-acec-4834-8bb9-248c7ba86331",
      "name": "OpenRouter Chat Model",
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "model": "mistralai/mistral-medium-3.1",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        672,
        656
      ],
      "id": "988b8b2e-a854-4b27-8c3f-fbabe3ae4e62",
      "name": "OpenRouter Chat Model1",
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "sendAndWait",
        "chatId": "1542517546",
        "message": "={{ $('Code in JavaScript').item.json.text }}\n\nTotal: {{ $('Code in JavaScript').item.json.total }}",
        "approvalOptions": {
          "values": {
            "approvalType": "double",
            "disapproveLabel": "\ud83d\udcdc Edit"
          }
        },
        "options": {}
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1840,
        432
      ],
      "id": "f4ce0107-bfc2-4efa-8da8-74211fafdab6",
      "name": "Send message and wait for response",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nlet execId = $execution.id;\nlet text = `\ud83e\uddfe **Neuer Kassenbon erkannt!**\\n`;\nlet total = 0;\nlet html = `<h2>Kassenbon korrigieren</h2>\n  <form action=\"https://beleriand.tailf188d3.ts.net/webhook/zettel-save\" method=\"POST\">\n  <input type=\"hidden\" name=\"receipt_id\" value=\"${execId}\">`\n\nfor (let i = 0; i < $input.first().json.output.length; i++) {\n  let item = $input.first().json.output[i];\n  const addText = `\n\ud83d\uded2 **${item.item}**\n\ud83d\udcb6 ${item.cost} \u20ac | \ud83c\udff7\ufe0f ${item.category}\n`\n  text += addText\n  total += item.cost\n\n  html += `<div style=\"margin-bottom: 10px;\">\n      <input type=\"text\" name=\"item_${i}\" value=\"${item.item}\">\n      <input type=\"text\" name=\"interpretation_${i}\" value=\"${item.interpretation}\">\n      <input type=\"number\" step=\"0.01\" name=\"cost_${i}\" value=\"${item.cost}\">\n      <input type=\"text\" name=\"category_${i}\" value=\"${item.category}\">\n      \n    </div>`;\n}\n\nhtml += `<button type=\"submit\">\u00c4nderungen speichern</button></form>`;\n\nreturn {text, total, html}"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1168,
        432
      ],
      "id": "ef72b2c1-9eef-4859-9480-d7640e3e17f3",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "html": "<!DOCTYPE html>\n\n<html>\n<head>\n  <meta charset=\"UTF-8\" />\n  <title>My HTML document</title>\n</head>\n<body>\n  <div class=\"container\">\n    {{ $json.html }}\n  </div>\n\n\n<style>\n  html {\n    color-scheme: dark;\n  }\n.container {\n  text-align: center;\n  padding: 16px;\n  border-radius: 8px;\n}\n\nh1 {\n  font-size: 24px;\n  font-weight: bold;\n  padding: 8px;\n}\n\nh2 {\n  font-size: 18px;\n  font-weight: bold;\n  padding: 8px;\n}\n</style>\n\n</body>\n</html>"
      },
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        1392,
        432
      ],
      "id": "e8b14f50-e295-4f11-9e80-ac46b3e4df7b",
      "name": "HTML"
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "value": "TrgvcHqyI6Aq2ZIU",
          "mode": "list",
          "cachedResultName": "kassenzettel",
          "cachedResultUrl": "/projects/XJIjlMZFCxfRf2Fm/datatables/TrgvcHqyI6Aq2ZIU"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "zettelhtml": "={{ $json.html }}",
            "zettelid": "={{ $execution.id }}"
          },
          "matchingColumns": [
            "zettelhtml"
          ],
          "schema": [
            {
              "id": "zettelhtml",
              "displayName": "zettelhtml",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "zettelid",
              "displayName": "zettelid",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        1616,
        432
      ],
      "id": "cbf0e56b-322e-4587-abb1-65cb654a6fff",
      "name": "Insert row"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "83b52001-f1e8-4359-abea-e4de740c40c5",
              "leftValue": "={{ $json.data.approved }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        2064,
        432
      ],
      "id": "17ef1508-8696-47b4-81d9-8b8b754f8562",
      "name": "If"
    },
    {
      "parameters": {
        "chatId": "1542517546",
        "text": "=https://beleriand.tailf188d3.ts.net/webhook/faae630f-84bf-4a36-86a8-3f1e33f20d1e/zettel-edit/{{ $execution.id }}",
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2288,
        528
      ],
      "id": "2cc6ab95-93ff-4446-a8c8-f54e93dab6fa",
      "name": "Send a text message",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1R4VH6g02lEJhxYuVqN7wcBmel0ogJsRMuIMstNEPEO8",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "https://docs.google.com/spreadsheets/d/1R4VH6g02lEJhxYuVqN7wcBmel0ogJsRMuIMstNEPEO8/edit?gid=0#gid=0",
          "mode": "url"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "item": "={{ $json.item }}",
            "cost": "={{ $json.cost }}",
            "category": "={{ $json.category }}",
            "date": "={{ $now }}",
            "zettelid": "={{ $('Insert row').item.json.zettelid }}",
            "day": "={{ $now.toFormat('yyyy-MM-dd') }}",
            "month": "={{ $now.toFormat('yyyy-MM') }}",
            "interpretation": "={{ $json.interpretation }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "zettelid",
              "displayName": "zettelid",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "interpretation",
              "displayName": "interpretation",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "item",
              "displayName": "item",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "cost",
              "displayName": "cost",
              "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": "date",
              "displayName": "date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "day",
              "displayName": "day",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "month",
              "displayName": "month",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "useAppend": true
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2736,
        336
      ],
      "id": "2fff9d79-c1ac-4428-abdf-79bf26b6aa2d",
      "name": "Append row in sheet",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "3bfcac38-bf34-4bf5-a460-e48886cc44a9",
              "name": "output",
              "value": "={{ $('AI Agent').item.json.output }}",
              "type": "array"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2288,
        336
      ],
      "id": "aa0a0047-a523-4adb-b94e-9e9a5397efb7",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "fieldToSplitOut": "output",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2512,
        336
      ],
      "id": "dab36c2d-08de-47ab-b9fb-13765f092f93",
      "name": "Split Out"
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Get a file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a file": {
      "main": [
        [
          {
            "node": "Analyze an image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Analyze an image": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send message and wait for response": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML": {
      "main": [
        [
          {
            "node": "Insert row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert row": {
      "main": [
        [
          {
            "node": "Send message and wait for response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send a text message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "0e82e404-eb24-4cad-a792-f13b12834ffe",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "7UJ9BU2UHgfAezsB",
  "tags": [
    {
      "updatedAt": "2026-03-22T10:40:47.895Z",
      "createdAt": "2026-03-22T10:40:47.895Z",
      "id": "Kx6dMYMFRgBgbM5c",
      "name": "kassenzettel"
    }
  ]
}