This workflow follows the Google Sheets → 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 →
{
"name": "E - Approval Handler",
"nodes": [
{
"parameters": {
"updates": [
"callback_query"
]
},
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
-500,
0
],
"id": "telegram-trigger-approval-1",
"name": "Approval Callback Listener",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const cq = $json.callback_query;\nif (!cq) { return []; }\nconst parts = (cq.data || '').split('|');\nconst approvalType = parts[0] || '';\nconst approvalId = parts[1] || '';\nconst decision = (parts[2] || '').toUpperCase();\nreturn [{\n json: {\n approvalType,\n approvalId,\n decision,\n callbackQueryId: cq.id,\n chatId: cq.message && cq.message.chat ? cq.message.chat.id : '',\n messageId: cq.message ? cq.message.message_id : '',\n originalText: cq.message ? (cq.message.text || '') : ''\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-260,
0
],
"id": "code-parse-callback-1",
"name": "Parse Callback"
},
{
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"value": "1lVPDfBIyFNGt3zKf3Xb3tUSaIRkIi6IZCKCtHtYj-c8",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "Approvals",
"mode": "name"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
-20,
0
],
"id": "sheets-read-approvals-1",
"name": "Read Approvals Sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const rows = $input.all();\nconst cb = $('Parse Callback').first().json;\nlet matchIndex = -1;\nfor (let i = 0; i < rows.length; i++) {\n if ((rows[i].json.ApprovalID || '') === cb.approvalId) { matchIndex = i; break; }\n}\nif (matchIndex === -1) {\n return [{ json: { ...cb, found: false, resultText: `${cb.originalText}\\n\\n\\u26A0\\uFE0F Couldn't find this approval (it may already be resolved).` } }];\n}\nconst row = rows[matchIndex].json;\nconst rowNumber = matchIndex + 2;\nconst newStatus = cb.decision === 'YES' ? 'Approved' : 'Backlog';\nconst resolvedDate = new Date().toISOString().slice(0,10);\nlet details = {};\ntry { details = JSON.parse(row.Details || '{}'); } catch (e) {}\nconst label = cb.decision === 'YES' ? '\\u2705 Approved \\u2014 moving forward' : '\\uD83D\\uDDC4 Saved to backlog for later';\nconst resultText = `${cb.originalText}\\n\\n${label}`;\nreturn [{\n json: {\n ...cb,\n found: true,\n rowNumber,\n newStatus,\n createdDate: row.CreatedDate || '',\n resolvedDate,\n approvalTypeStored: row.Type || cb.approvalType,\n details,\n resultText\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
220,
0
],
"id": "code-match-row-1",
"name": "Match Row & Build Update"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.found }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
460,
0
],
"id": "if-found-row-1",
"name": "Found Row?"
},
{
"parameters": {
"method": "PUT",
"url": "=https://sheets.googleapis.com/v4/spreadsheets/1lVPDfBIyFNGt3zKf3Xb3tUSaIRkIi6IZCKCtHtYj-c8/values/Approvals!D{{ $json.rowNumber }}:F{{ $json.rowNumber }}?valueInputOption=RAW",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googleSheetsOAuth2Api",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ values: [[ $json.newStatus, $json.createdDate, $json.resolvedDate ]] }) }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
700,
-100
],
"id": "http-update-approval-1",
"name": "Update Approval Status",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "callback",
"operation": "answerQuery",
"queryId": "={{ $('Match Row & Build Update').first().json.callbackQueryId }}"
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
940,
0
],
"id": "telegram-answer-query-1",
"name": "Answer Callback Query",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "message",
"operation": "editMessageText",
"chatId": "={{ $('Match Row & Build Update').first().json.chatId }}",
"messageId": "={{ $('Match Row & Build Update').first().json.messageId }}",
"text": "={{ $('Match Row & Build Update').first().json.resultText }}",
"reply_markup": "none"
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1180,
0
],
"id": "telegram-edit-message-1",
"name": "Edit Approval Message",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Approval Callback Listener": {
"main": [
[
{
"node": "Parse Callback",
"type": "main",
"index": 0
}
]
]
},
"Parse Callback": {
"main": [
[
{
"node": "Read Approvals Sheet",
"type": "main",
"index": 0
}
]
]
},
"Read Approvals Sheet": {
"main": [
[
{
"node": "Match Row & Build Update",
"type": "main",
"index": 0
}
]
]
},
"Match Row & Build Update": {
"main": [
[
{
"node": "Found Row?",
"type": "main",
"index": 0
}
]
]
},
"Found Row?": {
"main": [
[
{
"node": "Update Approval Status",
"type": "main",
"index": 0
}
],
[
{
"node": "Answer Callback Query",
"type": "main",
"index": 0
}
]
]
},
"Update Approval Status": {
"main": [
[
{
"node": "Answer Callback Query",
"type": "main",
"index": 0
}
]
]
},
"Answer Callback Query": {
"main": [
[
{
"node": "Edit Approval Message",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {}
}
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.
googleSheetsOAuth2ApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
E - Approval Handler. Uses telegramTrigger, googleSheets, httpRequest, telegram. Event-driven trigger; 8 nodes.
Source: https://gist.github.com/APalermo79/973b0e2d09e7b4163606d578015b1b61 — 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.
This workflow provides a complete solution for handling Telegram Stars payments, invoicing and refunds using n8n. It automates the process of sending invoices, managing pre-checkout approvals, recordi
clients kept booking meetings during my prayer times. i'd either miss a prayer or scramble to reschedule. the problem wasn't the clients — it was that my calendar had no blocked windows for salah. i n
Generate 360° product videos from a single photo using Google Veo 3 and Telegram
02b — Article callback. Uses telegramTrigger, googleSheets, telegram, httpRequest. Event-driven trigger; 30 nodes.
Automates LinkedIn job searches across multiple countries and categories, filters results with AI, stores data in Google Sheets, and sends weekly Telegram notifications. Perfect for professionals seek