This workflow follows the HTTP Request → Postgres 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 →
{
"name": "02 - Photo OCR Handler",
"nodes": [
{
"parameters": {
"updates": [
"message"
]
},
"id": "telegram-trigger-photo",
"name": "Telegram Photo Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.message.chat.id}}",
"value2": "={{$env.ALLOWED_CHAT_ID}}"
}
]
}
},
"id": "check-chat-id-photo",
"name": "Check Chat ID",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
450,
300
]
},
{
"parameters": {
"chatId": "={{$json.message.chat.id}}",
"text": "\ud83d\udcf8 Foto\u011fraf i\u015fleniyor..."
},
"id": "processing-message",
"name": "Send Processing Message",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
650,
200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"fileId": "={{$json.message.photo[$json.message.photo.length - 1].file_id}}"
},
"id": "get-photo",
"name": "Get Photo File",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
850,
200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{$env.TESSERACT_API_URL}}/ocr",
"options": {
"timeout": 30000
},
"sendBinaryData": true,
"binaryPropertyName": "data"
},
"id": "call-tesseract",
"name": "Call Tesseract OCR",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [
1050,
200
]
},
{
"parameters": {
"jsCode": "const ocrText = $input.first().json.text || '';\nreturn { json: { ocrText: ocrText.trim() } };"
},
"id": "extract-text",
"name": "Extract OCR Text",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1250,
200
]
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.ocrText}}",
"operation": "isNotEmpty"
}
]
}
},
"id": "check-ocr-success",
"name": "Check OCR Success",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
1450,
200
]
},
{
"parameters": {
"chatId": "={{$node[\"Telegram Photo Trigger\"].json.message.chat.id}}",
"text": "\u274c Foto\u011fraftan metin \u00e7\u0131kar\u0131lamad\u0131. L\u00fctfen daha net bir foto\u011fraf g\u00f6nderin veya manuel olarak giri\u015f yap\u0131n."
},
"id": "ocr-fail-message",
"name": "Send OCR Fail Message",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1650,
300
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"filePath": "/prompts/finance_parser.txt"
},
"id": "read-prompt-photo",
"name": "Read Prompt",
"type": "n8n-nodes-base.readBinaryFile",
"typeVersion": 1,
"position": [
1650,
100
]
},
{
"parameters": {
"method": "POST",
"url": "={{$env.OLLAMA_BASE_URL}}/api/generate",
"options": {
"timeout": 30000
},
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "llama3.1"
},
{
"name": "prompt",
"value": "={{$node[\"Read Prompt\"].json.data}}\\n\\nOCR \u00c7\u0131kt\u0131s\u0131: {{$node[\"Extract OCR Text\"].json.ocrText}}\\n\\nSadece JSON d\u00f6nd\u00fcr, ba\u015fka a\u00e7\u0131klama yapma."
},
{
"name": "stream",
"value": false
}
]
}
},
"id": "call-ollama-photo",
"name": "Call Ollama LLM",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [
1850,
100
]
},
{
"parameters": {
"jsCode": "const response = $input.first().json.response;\nconst parsedData = JSON.parse(response);\n\n// Generate fingerprint\nconst crypto = require('crypto');\nconst fingerprintString = `${parsedData.date}_${parsedData.amount}_${parsedData.description}`;\nconst fingerprint = crypto.createHash('sha256').update(fingerprintString).digest('hex');\n\nparsedData.fingerprint = fingerprint;\nparsedData.telegram_message_id = $('Telegram Photo Trigger').first().json.message.message_id;\nparsedData.raw_input = $('Extract OCR Text').first().json.ocrText;\nparsedData.source = 'photo';\n\nreturn { json: parsedData };"
},
"id": "parse-response-photo",
"name": "Parse LLM Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2050,
100
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT id FROM {{$json.type}}s WHERE fingerprint = '{{$json.fingerprint}}' LIMIT 1"
},
"id": "check-duplicate-photo",
"name": "Check Duplicate",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
2250,
100
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{$json.length}}",
"operation": "equal",
"value2": 0
}
]
}
},
"id": "is-not-duplicate-photo",
"name": "Is Not Duplicate",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
2450,
100
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO {{$node[\"Parse LLM Response\"].json.type}}s (telegram_message_id, date, amount, currency, category, subcategory, bank, payment_method, source_name, description, fingerprint, confidence_score, source, raw_input, asset_type, asset_name, transaction_type, quantity, unit_price) VALUES ({{$node[\"Parse LLM Response\"].json.telegram_message_id}}, '{{$node[\"Parse LLM Response\"].json.date}}', {{$node[\"Parse LLM Response\"].json.amount}}, '{{$node[\"Parse LLM Response\"].json.currency}}', '{{$node[\"Parse LLM Response\"].json.category}}', '{{$node[\"Parse LLM Response\"].json.subcategory}}', '{{$node[\"Parse LLM Response\"].json.bank}}', '{{$node[\"Parse LLM Response\"].json.payment_method}}', '{{$node[\"Parse LLM Response\"].json.source_name}}', '{{$node[\"Parse LLM Response\"].json.description}}', '{{$node[\"Parse LLM Response\"].json.fingerprint}}', {{$node[\"Parse LLM Response\"].json.confidence}}, '{{$node[\"Parse LLM Response\"].json.source}}', '{{$node[\"Parse LLM Response\"].json.raw_input}}', '{{$node[\"Parse LLM Response\"].json.asset_type}}', '{{$node[\"Parse LLM Response\"].json.asset_name}}', '{{$node[\"Parse LLM Response\"].json.transaction_type}}', {{$node[\"Parse LLM Response\"].json.quantity}}, {{$node[\"Parse LLM Response\"].json.unit_price}}) RETURNING id"
},
"id": "insert-db-photo",
"name": "Insert to Database",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
2650,
100
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{$node[\"Telegram Photo Trigger\"].json.message.chat.id}}",
"text": "\u2705 Foto\u011fraftan kay\u0131t eklendi!\\n\\n\ud83d\udcca T\u00fcr: {{$node[\"Parse LLM Response\"].json.type}}\\n\ud83d\udcb0 Tutar: {{$node[\"Parse LLM Response\"].json.amount}} {{$node[\"Parse LLM Response\"].json.currency}}\\n\ud83d\udcc1 Kategori: {{$node[\"Parse LLM Response\"].json.category}}\\n\ud83d\udcdd A\u00e7\u0131klama: {{$node[\"Parse LLM Response\"].json.description}}\\n\ud83d\udcc5 Tarih: {{$node[\"Parse LLM Response\"].json.date}}"
},
"id": "send-success-photo",
"name": "Send Success Message",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
2850,
100
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{$node[\"Telegram Photo Trigger\"].json.message.chat.id}}",
"text": "\u26a0\ufe0f Bu kay\u0131t zaten mevcut! Tekrar eklenmedi."
},
"id": "send-duplicate-photo",
"name": "Send Duplicate Message",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
2650,
200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Telegram Photo Trigger": {
"main": [
[
{
"node": "Check Chat ID",
"type": "main",
"index": 0
}
]
]
},
"Check Chat ID": {
"main": [
[
{
"node": "Send Processing Message",
"type": "main",
"index": 0
}
]
]
},
"Send Processing Message": {
"main": [
[
{
"node": "Get Photo File",
"type": "main",
"index": 0
}
]
]
},
"Get Photo File": {
"main": [
[
{
"node": "Call Tesseract OCR",
"type": "main",
"index": 0
}
]
]
},
"Call Tesseract OCR": {
"main": [
[
{
"node": "Extract OCR Text",
"type": "main",
"index": 0
}
]
]
},
"Extract OCR Text": {
"main": [
[
{
"node": "Check OCR Success",
"type": "main",
"index": 0
}
]
]
},
"Check OCR Success": {
"main": [
[
{
"node": "Read Prompt",
"type": "main",
"index": 0
}
],
[
{
"node": "Send OCR Fail Message",
"type": "main",
"index": 0
}
]
]
},
"Read Prompt": {
"main": [
[
{
"node": "Call Ollama LLM",
"type": "main",
"index": 0
}
]
]
},
"Call Ollama LLM": {
"main": [
[
{
"node": "Parse LLM Response",
"type": "main",
"index": 0
}
]
]
},
"Parse LLM Response": {
"main": [
[
{
"node": "Check Duplicate",
"type": "main",
"index": 0
}
]
]
},
"Check Duplicate": {
"main": [
[
{
"node": "Is Not Duplicate",
"type": "main",
"index": 0
}
]
]
},
"Is Not Duplicate": {
"main": [
[
{
"node": "Insert to Database",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Duplicate Message",
"type": "main",
"index": 0
}
]
]
},
"Insert to Database": {
"main": [
[
{
"node": "Send Success Message",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 1,
"updatedAt": "2026-02-07T23:34:27.213Z",
"versionId": "1"
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
postgrestelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
02 - Photo OCR Handler. Uses telegramTrigger, telegram, httpRequest, readBinaryFile. Event-driven trigger; 16 nodes.
Source: https://github.com/nebikiramanli/personal-finance-bot/blob/ed72282f85eb918b1c7e959e01cea3e30c696d96/n8n/workflows/02_photo_ocr.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Pede Ai. Uses httpRequest, telegram, postgres, telegramTrigger. Event-driven trigger; 53 nodes.
Extend N8N With Additional Tools. Uses telegram, telegramTrigger, httpRequest, spreadsheetFile. Event-driven trigger; 21 nodes.
This workflow extends n8n and uses R language graphic capabilities. This is a Telegram bot which fetches weather data via the openweathermap.org API, plots an image using ggoplot2 package from R and s
N8N Complete Final. Uses telegramTrigger, dataTable, telegram, mqtt. Event-driven trigger; 58 nodes.
TextMain. Uses telegramTrigger, stopAndError, telegram, httpRequest. Event-driven trigger; 56 nodes.