This workflow follows the HTTP Request → Telegram 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": "MercadoChat - Fluxo Principal",
"nodes": [
{
"parameters": {
"updates": [
"message",
"photo"
],
"additionalFields": {}
},
"id": "telegram-trigger",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
250,
400
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Extrai dados da mensagem do Telegram\nconst message = $input.first().json;\n\nconst telegramId = message?.message?.from?.id || message?.message?.chat?.id;\nconst username = message?.message?.from?.username || null;\nconst firstName = message?.message?.from?.first_name || 'usu\u00e1rio';\nconst messageText = message?.message?.text || '';\nconst hasPhoto = !!(message?.message?.photo);\nconst chatId = message?.message?.chat?.id;\nconst messageId = message?.message?.message_id;\n\n// Pega a maior foto dispon\u00edvel\nlet photoFileId = null;\nif (hasPhoto) {\n const photos = message.message.photo;\n photoFileId = photos[photos.length - 1].file_id;\n}\n\nreturn [{\n json: {\n telegramId,\n username,\n firstName,\n messageText,\n hasPhoto,\n photoFileId,\n chatId,\n messageId\n }\n}];"
},
"id": "extract-message",
"name": "Extrair Dados da Mensagem",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
470,
400
]
},
{
"parameters": {
"method": "GET",
"url": "=https://lhybdghiuafcsvytxhdp.supabase.co/rest/v1/users",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "<redacted-credential>"
},
{
"name": "Authorization",
"value": "<redacted-credential>"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "telegram_id",
"value": "=eq.{{ $json.telegramId }}"
},
{
"name": "select",
"value": "*"
}
]
}
},
"id": "check-user",
"name": "Verificar Usu\u00e1rio no Supabase",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
690,
400
]
},
{
"parameters": {
"jsCode": "// Verifica o estado do usu\u00e1rio e define o pr\u00f3ximo passo\nconst msg = $('Extrair Dados da Mensagem').first().json;\nconst users = $input.first().json;\n\nconst userList = Array.isArray(users) ? users : [];\nconst user = userList.length > 0 ? userList[0] : null;\n\nlet routeTo = 'intent';\nlet context = {\n isNewUser: !user,\n isRegistered: user?.is_registered || false,\n registrationStep: user?.registration_step || 'start',\n userId: user?.id || null,\n userPoints: user?.points || 0,\n userName: user?.full_name || msg.firstName\n};\n\n// Verifica se est\u00e1 no fluxo de cadastro ou se \u00e9 novo usu\u00e1rio\nif (!user || !user.is_registered) {\n routeTo = 'registration';\n} else if (msg.hasPhoto) {\n routeTo = 'photo';\n} else {\n routeTo = 'intent';\n}\n\nreturn [{ json: { ...msg, ...context, routeTo } }];"
},
"id": "check-registration",
"name": "Verificar Estado do Usu\u00e1rio",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
910,
400
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"leftValue": "={{ $json.routeTo }}",
"rightValue": "registration",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
},
"outputIndex": 0
},
{
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"leftValue": "={{ $json.routeTo }}",
"rightValue": "photo",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
},
"outputIndex": 1
},
{
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"leftValue": "={{ $json.routeTo }}",
"rightValue": "intent",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
},
"outputIndex": 2
}
]
}
},
"id": "router-main",
"name": "Roteador Principal",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
1130,
400
]
},
{
"parameters": {
"jsCode": "// L\u00f3gica de cadastro por etapas\nconst data = $input.first().json;\nconst step = data.registrationStep || 'start';\nconst message = data.messageText?.trim() || '';\nconst telegramId = data.telegramId;\nconst chatId = data.chatId;\n\nlet response = '';\nlet nextStep = step;\nlet updateData = {};\nlet shouldUpdate = false;\n\nswitch (step) {\n case 'start':\n // Novo usu\u00e1rio - criar registro e pedir nome\n response = `Ol\u00e1, ${data.firstName}! \ud83d\udc4b Bem-vindo ao *MercadoBot*! \ud83d\uded2\\n\\nVou precisar de alguns dados para criar sua conta.\\n\\n\ud83d\udcdd Qual \u00e9 o seu *nome completo*?`;\n nextStep = 'waiting_name';\n shouldUpdate = true;\n updateData = { registration_step: 'waiting_name' };\n break;\n \n case 'waiting_name':\n if (message.length < 3) {\n response = '\u274c Nome muito curto. Por favor, informe seu nome completo.';\n } else {\n response = `Obrigado, *${message}*! \ud83d\ude0a\\n\\n\ud83d\udd22 Agora informe seu *CPF* (apenas n\u00fameros):`;\n nextStep = 'waiting_cpf';\n shouldUpdate = true;\n updateData = { full_name: message, registration_step: 'waiting_cpf' };\n }\n break;\n \n case 'waiting_cpf':\n const cpfClean = message.replace(/\\D/g, '');\n if (cpfClean.length !== 11) {\n response = '\u274c CPF inv\u00e1lido. Por favor, informe os 11 d\u00edgitos do CPF.';\n } else {\n response = '\ud83d\udcc5 \u00d3timo! Agora informe sua *data de nascimento* no formato DD/MM/AAAA:';\n nextStep = 'waiting_birthdate';\n shouldUpdate = true;\n updateData = { cpf: cpfClean, registration_step: 'waiting_birthdate' };\n }\n break;\n \n case 'waiting_birthdate':\n const dateparts = message.split('/');\n if (dateparts.length !== 3 || dateparts[2].length !== 4) {\n response = '\u274c Formato inv\u00e1lido. Por favor use DD/MM/AAAA (ex: 15/03/1990):';\n } else {\n const birthDate = `${dateparts[2]}-${dateparts[1].padStart(2,'0')}-${dateparts[0].padStart(2,'0')}`;\n response = `\u2705 *Cadastro conclu\u00eddo!*\\n\\nSeja bem-vindo ao MercadoBot! \ud83c\udf89\\n\\nAgora voc\u00ea pode:\\n\u2022 \ud83d\uded2 Consultar pre\u00e7os de produtos\\n\u2022 \ud83d\udcca Comparar pre\u00e7os entre mercados\\n\u2022 \ud83d\udcf8 Enviar fotos de notas fiscais para ganhar *pontos*\\n\u2022 \ud83c\udf81 Trocar pontos por cashback e sorteios!\\n\\nEnvie /ajuda para ver todos os comandos.`;\n nextStep = 'completed';\n shouldUpdate = true;\n updateData = { birth_date: birthDate, registration_step: 'completed', is_registered: true };\n }\n break;\n \n default:\n response = '\u26a0\ufe0f Ocorreu um erro no cadastro. Por favor, entre em contato com o suporte.';\n}\n\nreturn [{ json: { response, nextStep, updateData, shouldUpdate, telegramId, chatId, registrationStep: step, isNewUser: data.isNewUser } }];"
},
"id": "registration-logic",
"name": "L\u00f3gica de Cadastro",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1350,
200
]
},
{
"parameters": {
"conditions": {
"options": {},
"conditions": [
{
"leftValue": "={{ $json.isNewUser }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
]
}
},
"id": "is-new-user",
"name": "\u00c9 Novo Usu\u00e1rio?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1570,
200
]
},
{
"parameters": {
"method": "POST",
"url": "=https://lhybdghiuafcsvytxhdp.supabase.co/rest/v1/users",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "<redacted-credential>"
},
{
"name": "Authorization",
"value": "<redacted-credential>"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Prefer",
"value": "return=minimal"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "telegram_id",
"value": "={{ $('L\u00f3gica de Cadastro').first().json.telegramId }}"
},
{
"name": "registration_step",
"value": "waiting_name"
}
]
}
},
"id": "create-user",
"name": "Criar Usu\u00e1rio no Supabase",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1790,
100
]
},
{
"parameters": {
"method": "PATCH",
"url": "=https://lhybdghiuafcsvytxhdp.supabase.co/rest/v1/users?telegram_id=eq.{{ $('L\u00f3gica de Cadastro').first().json.telegramId }}",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "<redacted-credential>"
},
{
"name": "Authorization",
"value": "<redacted-credential>"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Prefer",
"value": "return=minimal"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "=body",
"value": "={{ JSON.stringify($('L\u00f3gica de Cadastro').first().json.updateData) }}"
}
]
}
},
"id": "update-user-registration",
"name": "Atualizar Registro no Supabase",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1790,
300
]
},
{
"parameters": {
"chatId": "={{ $('L\u00f3gica de Cadastro').first().json.chatId }}",
"text": "={{ $('L\u00f3gica de Cadastro').first().json.response }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "send-registration-response",
"name": "Enviar Resposta de Cadastro",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
2010,
200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=<redacted-credential>",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "=body",
"value": "={{ JSON.stringify({ contents: [{ parts: [{ text: `Voc\u00ea \u00e9 um assistente de compras de supermercado. Analise a mensagem do usu\u00e1rio e retorne APENAS um JSON com a inten\u00e7\u00e3o identificada. Sem texto adicional, sem markdown.\\n\\nInten\u00e7\u00f5es: buy_item, price_history, find_item, nearby_markets, price_comparison, send_receipt, points_balance, points_redeem, help, greeting, unknown\\n\\nFormato:\\n{\\\"intent\\\": \\\"<intent>\\\", \\\"entities\\\": {\\\"product\\\": \\\"<produto ou null>\\\", \\\"market\\\": \\\"<mercado ou null>\\\", \\\"cep\\\": \\\"<cep ou null>\\\"}, \\\"confidence\\\": <0.0 a 1.0>}\\n\\nMensagem: ${$json.messageText}` }] }] }) }}"
}
]
}
},
"id": "gemini-intent",
"name": "Gemini - Identificar Inten\u00e7\u00e3o",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1350,
400
]
},
{
"parameters": {
"jsCode": "// Parse a resposta do Gemini\nconst geminiResponse = $input.first().json;\nconst msgData = $('Verificar Estado do Usu\u00e1rio').first().json;\n\nlet intentData = { intent: 'unknown', entities: {}, confidence: 0 };\n\ntry {\n const rawText = geminiResponse?.candidates?.[0]?.content?.parts?.[0]?.text || '{}';\n const cleanText = rawText.replace(/```json/g, '').replace(/```/g, '').trim();\n intentData = JSON.parse(cleanText);\n} catch(e) {\n intentData = { intent: 'unknown', entities: {}, confidence: 0 };\n}\n\nreturn [{ json: {\n ...msgData,\n intent: intentData.intent || 'unknown',\n entities: intentData.entities || {},\n confidence: intentData.confidence || 0\n}}];"
},
"id": "parse-intent",
"name": "Parse Inten\u00e7\u00e3o do Gemini",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1570,
400
]
},
{
"parameters": {
"rules": {
"values": [
{
"outputIndex": 0,
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intent }}",
"rightValue": "price_history",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
},
{
"outputIndex": 1,
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intent }}",
"rightValue": "find_item",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
},
{
"outputIndex": 1,
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intent }}",
"rightValue": "buy_item",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
},
{
"outputIndex": 2,
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intent }}",
"rightValue": "nearby_markets",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
},
{
"outputIndex": 3,
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intent }}",
"rightValue": "price_comparison",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
},
{
"outputIndex": 4,
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intent }}",
"rightValue": "points_balance",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
}
]
},
"fallbackOutput": 5
},
"id": "intent-switch",
"name": "Switch de Inten\u00e7\u00f5es",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
1790,
400
]
},
{
"parameters": {
"method": "GET",
"url": "=https://lhybdghiuafcsvytxhdp.supabase.co/rest/v1/purchase_items",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "<redacted-credential>"
},
{
"name": "Authorization",
"value": "<redacted-credential>"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "select",
"value": "unit_price,total_price,quantity,created_at,products(name),purchases!inner(purchase_date,user_id,markets(name,city))"
},
{
"name": "purchases.user_id",
"value": "=eq.{{ $json.userId }}"
},
{
"name": "products.name_normalized",
"value": "=ilike.*{{ $json.entities?.product?.toLowerCase() || \"\" }}*"
},
{
"name": "order",
"value": "purchases.purchase_date.desc"
},
{
"name": "limit",
"value": "10"
}
]
}
},
"id": "query-price-history",
"name": "Buscar Hist\u00f3rico de Pre\u00e7os",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2010,
200
]
},
{
"parameters": {
"method": "GET",
"url": "=https://lhybdghiuafcsvytxhdp.supabase.co/rest/v1/price_records",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "<redacted-credential>"
},
{
"name": "Authorization",
"value": "<redacted-credential>"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "select",
"value": "price,recorded_at,markets(name,city,address),products(name)"
},
{
"name": "products.name_normalized",
"value": "=ilike.*{{ $json.entities?.product?.toLowerCase() || \"\" }}*"
},
{
"name": "markets.name_normalized",
"value": "=ilike.*{{ $json.entities?.market?.toLowerCase() || \"\" }}*"
},
{
"name": "order",
"value": "price.asc,recorded_at.desc"
},
{
"name": "limit",
"value": "15"
}
]
}
},
"id": "query-find-item",
"name": "Buscar Onde Encontrar Item",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2010,
340
]
},
{
"parameters": {
"method": "GET",
"url": "=https://lhybdghiuafcsvytxhdp.supabase.co/rest/v1/markets",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "<redacted-credential>"
},
{
"name": "Authorization",
"value": "<redacted-credential>"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "select",
"value": "*"
},
{
"name": "cep",
"value": "=ilike.{{ ($json.entities?.cep || \"\").replace(/\\D/g, \"\") }}*"
},
{
"name": "limit",
"value": "10"
}
]
}
},
"id": "query-nearby-markets",
"name": "Buscar Mercados Pr\u00f3ximos",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2010,
480
]
},
{
"parameters": {
"method": "GET",
"url": "=https://lhybdghiuafcsvytxhdp.supabase.co/rest/v1/price_records",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "=sb_publishable_omUBTYHoL5mxrpNe_AlNbQ_DS7FeBQt"
},
{
"name": "Authorization",
"value": "=<redacted-credential>"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "select",
"value": "price,recorded_at,markets(name,city),products(name)"
},
{
"name": "products.name_normalized",
"value": "=ilike.*{{ $json.entities?.product?.toLowerCase() || \"\" }}*"
},
{
"name": "order",
"value": "price.asc,recorded_at.desc"
},
{
"name": "limit",
"value": "20"
}
]
}
},
"id": "query-price-comparison",
"name": "Buscar Comparativo de Pre\u00e7os",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2010,
600
]
},
{
"parameters": {
"jsCode": "// Monta resposta de saldo de pontos\nconst data = $('Parse Inten\u00e7\u00e3o do Gemini').first().json;\nconst points = data.userPoints || 0;\nconst pointsConfig = {\n cashbackRate: 100, // pontos por real\n raffleTicket: 500 // pontos por cupom\n};\n\nconst cashbackValue = (points / pointsConfig.cashbackRate).toFixed(2);\nconst raffleTickets = Math.floor(points / pointsConfig.raffleTicket);\nconst pointsToNextTicket = pointsConfig.raffleTicket - (points % pointsConfig.raffleTicket);\n\nconst response = `\ud83c\udfaf *Seus Pontos MercadoBot*\\n\\n\ud83d\udcb0 Saldo atual: *${points} pontos*\\n\ud83d\udcb5 Equivale a: *R$ ${cashbackValue}* em cashback\\n\ud83c\udf9f\ufe0f Cupons de sorteio: *${raffleTickets}*\\n\\n\ud83d\udcc8 Faltam *${pointsToNextTicket} pontos* para o pr\u00f3ximo cupom!\\n\\n\ud83d\udcf8 Envie fotos de notas fiscais para acumular mais pontos!`;\n\nreturn [{ json: { response, chatId: data.chatId } }];"
},
"id": "points-balance-response",
"name": "Resposta de Saldo de Pontos",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2010,
720
]
},
{
"parameters": {
"jsCode": "// Resposta para inten\u00e7\u00f5es n\u00e3o reconhecidas ou ajuda\nconst data = $('Parse Inten\u00e7\u00e3o do Gemini').first().json;\nconst intent = data.intent;\nconst name = data.userName || 'usu\u00e1rio';\n\nlet response = '';\n\nif (intent === 'greeting') {\n response = `Ol\u00e1, ${name}! \ud83d\udc4b\\n\\nSou o *MercadoBot*, seu assistente de compras! \ud83d\uded2\\n\\nPosso ajudar com:\\n\u2022 \ud83d\udcb0 Hist\u00f3rico de pre\u00e7os de produtos\\n\u2022 \ud83d\udd0d Onde encontrar itens\\n\u2022 \ud83d\udcca Comparar pre\u00e7os\\n\u2022 \ud83c\udfea Mercados pr\u00f3ximos\\n\u2022 \ud83d\udcf8 Notas fiscais (ganhe pontos!)\\n\u2022 \ud83c\udf81 Consultar seus pontos\\n\\nComo posso ajudar?`;\n} else if (intent === 'help') {\n response = `\ud83d\udcd6 *O que posso fazer por voc\u00ea:*\\n\\n\u2022 _Quanto paguei no arroz?_\\n\u2022 _Onde tem leite mais barato?_\\n\u2022 _Mercados pr\u00f3ximos ao CEP 01310-100_\\n\u2022 _Comparar pre\u00e7o do azeite_\\n\u2022 _Meus pontos_\\n\u2022 \ud83d\udcf8 Envie uma foto de nota fiscal!`;\n} else {\n response = `Hmm, n\u00e3o entendi muito bem. \ud83e\udd14\\n\\nTente perguntar de outra forma, por exemplo:\\n\u2022 _Quanto paguei no leite no Carrefour?_\\n\u2022 _Onde tem arroz?_\\n\u2022 _Meus pontos_\\n\\nOu envie /ajuda para ver todos os comandos.`;\n}\n\nreturn [{ json: { response, chatId: data.chatId } }];"
},
"id": "fallback-response",
"name": "Resposta Padr\u00e3o / Ajuda",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2010,
850
]
},
{
"parameters": {
"jsCode": "// Formata os dados do Supabase e gera resposta via Gemini\nconst intentData = $('Parse Inten\u00e7\u00e3o do Gemini').first().json;\nconst dbData = $input.first().json;\nconst intent = intentData.intent;\nconst entities = intentData.entities || {};\n\n// Prepara o texto para o Gemini gerar a resposta\nconst systemPrompt = `Voc\u00ea \u00e9 o MercadoBot, assistente de compras. Responda em portugu\u00eas de forma amig\u00e1vel e clara, m\u00e1ximo 500 caracteres. Use emojis com modera\u00e7\u00e3o.`;\n\nconst userPrompt = `Intent: ${intent}\\nProduto buscado: ${entities.product || 'n\u00e3o informado'}\\nMercado: ${entities.market || 'n\u00e3o informado'}\\nDados: ${JSON.stringify(dbData).substring(0, 2000)}\\n\\nGere uma resposta \u00fatil para o usu\u00e1rio baseada nesses dados.`;\n\nreturn [{ json: { systemPrompt, userPrompt, chatId: intentData.chatId, intent } }];"
},
"id": "prepare-gemini-response",
"name": "Preparar Dados para Gemini",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2230,
400
]
},
{
"parameters": {
"method": "POST",
"url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=<redacted-credential>",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "=body",
"value": "={{ JSON.stringify({ contents: [{ parts: [{ text: $json.systemPrompt + '\\n\\n' + $json.userPrompt }] }] }) }}"
}
]
}
},
"id": "gemini-response",
"name": "Gemini - Gerar Resposta",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2450,
400
]
},
{
"parameters": {
"jsCode": "const geminiResp = $input.first().json;\nconst prepData = $('Preparar Dados para Gemini').first().json;\n\nlet responseText = '';\ntry {\n responseText = geminiResp?.candidates?.[0]?.content?.parts?.[0]?.text || '\u274c Erro ao gerar resposta.';\n} catch(e) {\n responseText = '\u274c Erro ao processar sua solicita\u00e7\u00e3o. Tente novamente.';\n}\n\nreturn [{ json: { response: responseText, chatId: prepData.chatId } }];"
},
"id": "parse-gemini-response",
"name": "Parse Resposta Gemini",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2670,
400
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.response }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "send-final-response",
"name": "Enviar Resposta Final",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
2890,
400
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "=COLE_AQUI_A_URL_DO_WEBHOOK_DO_FLUXO_2_DEPOIS_DE_SALVAR_E_ATIVAR_ELE",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "=body",
"value": "={{ JSON.stringify({ telegramId: $json.telegramId, userId: $json.userId, chatId: $json.chatId, photoFileId: $json.photoFileId, firstName: $json.firstName, telegramBotToken: '8733282592:AAF7232MFeMdRcV6HyHUeKvyTKe71cv8oNw', geminiApiKey: '<redacted-credential>', supabaseUrl: 'https://lhybdghiuafcsvytxhdp.supabase.co', supabaseKey: 'sb_publishable_omUBTYHoL5mxrpNe_AlNbQ_DS7FeBQt' }) }}"
}
]
}
},
"id": "call-receipt-workflow",
"name": "Chamar Fluxo de Nota Fiscal",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1350,
620
],
"continueOnFail": true
},
{
"parameters": {
"jsCode": "const receiptResult = $input.first().json || {};\nconst upstream = $('Verificar Estado do Usu?rio').first().json;\n\nconst responseText =\n receiptResult.response ||\n receiptResult.message ||\n receiptResult.body?.response ||\n 'Recebi sua nota fiscal, mas ocorreu um erro no processamento. Tente novamente em alguns minutos.';\n\nreturn [{\n json: {\n chatId: upstream.chatId,\n response: responseText,\n },\n}];"
},
"id": "parse-receipt-response",
"name": "Parse Retorno Nota Fiscal",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1570,
620
]
}
],
"connections": {
"Telegram Trigger": {
"main": [
[
{
"node": "Extrair Dados da Mensagem",
"type": "main",
"index": 0
}
]
]
},
"Extrair Dados da Mensagem": {
"main": [
[
{
"node": "Verificar Usu\u00e1rio no Supabase",
"type": "main",
"index": 0
}
]
]
},
"Verificar Usu\u00e1rio no Supabase": {
"main": [
[
{
"node": "Verificar Estado do Usu\u00e1rio",
"type": "main",
"index": 0
}
]
]
},
"Verificar Estado do Usu\u00e1rio": {
"main": [
[
{
"node": "Roteador Principal",
"type": "main",
"index": 0
}
]
]
},
"Roteador Principal": {
"main": [
[
{
"node": "L\u00f3gica de Cadastro",
"type": "main",
"index": 0
}
],
[
{
"node": "Chamar Fluxo de Nota Fiscal",
"type": "main",
"index": 0
}
],
[
{
"node": "Gemini - Identificar Inten\u00e7\u00e3o",
"type": "main",
"index": 0
}
]
]
},
"L\u00f3gica de Cadastro": {
"main": [
[
{
"node": "\u00c9 Novo Usu\u00e1rio?",
"type": "main",
"index": 0
}
]
]
},
"\u00c9 Novo Usu\u00e1rio?": {
"main": [
[
{
"node": "Criar Usu\u00e1rio no Supabase",
"type": "main",
"index": 0
}
],
[
{
"node": "Atualizar Registro no Supabase",
"type": "main",
"index": 0
}
]
]
},
"Criar Usu\u00e1rio no Supabase": {
"main": [
[
{
"node": "Enviar Resposta de Cadastro",
"type": "main",
"index": 0
}
]
]
},
"Atualizar Registro no Supabase": {
"main": [
[
{
"node": "Enviar Resposta de Cadastro",
"type": "main",
"index": 0
}
]
]
},
"Gemini - Identificar Inten\u00e7\u00e3o": {
"main": [
[
{
"node": "Parse Inten\u00e7\u00e3o do Gemini",
"type": "main",
"index": 0
}
]
]
},
"Parse Inten\u00e7\u00e3o do Gemini": {
"main": [
[
{
"node": "Switch de Inten\u00e7\u00f5es",
"type": "main",
"index": 0
}
]
]
},
"Switch de Inten\u00e7\u00f5es": {
"main": [
[
{
"node": "Buscar Hist\u00f3rico de Pre\u00e7os",
"type": "main",
"index": 0
}
],
[
{
"node": "Buscar Onde Encontrar Item",
"type": "main",
"index": 0
}
],
[
{
"node": "Buscar Mercados Pr\u00f3ximos",
"type": "main",
"index": 0
}
],
[
{
"node": "Buscar Comparativo de Pre\u00e7os",
"type": "main",
"index": 0
}
],
[
{
"node": "Resposta de Saldo de Pontos",
"type": "main",
"index": 0
}
],
[
{
"node": "Resposta Padr\u00e3o / Ajuda",
"type": "main",
"index": 0
}
]
]
},
"Buscar Hist\u00f3rico de Pre\u00e7os": {
"main": [
[
{
"node": "Preparar Dados para Gemini",
"type": "main",
"index": 0
}
]
]
},
"Buscar Onde Encontrar Item": {
"main": [
[
{
"node": "Preparar Dados para Gemini",
"type": "main",
"index": 0
}
]
]
},
"Buscar Mercados Pr\u00f3ximos": {
"main": [
[
{
"node": "Preparar Dados para Gemini",
"type": "main",
"index": 0
}
]
]
},
"Buscar Comparativo de Pre\u00e7os": {
"main": [
[
{
"node": "Preparar Dados para Gemini",
"type": "main",
"index": 0
}
]
]
},
"Preparar Dados para Gemini": {
"main": [
[
{
"node": "Gemini - Gerar Resposta",
"type": "main",
"index": 0
}
]
]
},
"Gemini - Gerar Resposta": {
"main": [
[
{
"node": "Parse Resposta Gemini",
"type": "main",
"index": 0
}
]
]
},
"Parse Resposta Gemini": {
"main": [
[
{
"node": "Enviar Resposta Final",
"type": "main",
"index": 0
}
]
]
},
"Resposta de Saldo de Pontos": {
"main": [
[
{
"node": "Enviar Resposta Final",
"type": "main",
"index": 0
}
]
]
},
"Resposta Padr\u00e3o / Ajuda": {
"main": [
[
{
"node": "Enviar Resposta Final",
"type": "main",
"index": 0
}
]
]
},
"Chamar Fluxo de Nota Fiscal": {
"main": [
[
{
"node": "Parse Retorno Nota Fiscal",
"type": "main",
"index": 0
}
]
]
},
"Parse Retorno Nota Fiscal": {
"main": [
[
{
"node": "Enviar Resposta Final",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
"mercado-chat"
],
"triggerCount": 1,
"updatedAt": "2026-03-06T00:00:00.000Z",
"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.
telegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
MercadoChat - Fluxo Principal. Uses telegramTrigger, httpRequest, telegram. Event-driven trigger; 25 nodes.
Source: https://github.com/Shtorache/mercadinho_chat/blob/f596a308e1f9f899d9a51a3df1ece34e9b5b9b34/n8n_workflows/01_fluxo_principal.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.
N8N Complete Final. Uses telegramTrigger, dataTable, telegram, mqtt. Event-driven trigger; 58 nodes.
TextMain. Uses telegramTrigger, stopAndError, telegram, httpRequest. Event-driven trigger; 56 nodes.
Pede Ai. Uses httpRequest, telegram, postgres, telegramTrigger. Event-driven trigger; 53 nodes.
📄 Documentation: Notion Guide
Telegram Wait. Uses stickyNote, httpRequest, redis, noOp. Event-driven trigger; 36 nodes.