{
  "name": "Telegram HITL Approval Handler",
  "nodes": [
    {
      "parameters": {
        "updates": [
          "callback_query"
        ],
        "additionalFields": {}
      },
      "id": "trigger-callback",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "notes": "One consumer per bot token. Only this trigger should listen on callback_query for this bot."
    },
    {
      "parameters": {
        "jsCode": "const cq = $input.first().json.callback_query || $input.first().json;\nconst data = cq.data || '';\nconst [decision, ...rest] = data.split(':');\nconst jobId = rest.join(':');\nconst isApprove = decision === 'APPROVE';\nconst isDeny = decision === 'DENY';\nreturn [{ json: {\n  callbackId: cq.id,\n  chatId: String(cq.message?.chat?.id || ''),\n  messageId: cq.message?.message_id,\n  messageText: cq.message?.text || cq.message?.caption || '',\n  jobId,\n  decision,\n  isApprove,\n  isDeny,\n  recognized: isApprove || isDeny\n} }];"
      },
      "id": "parse-callback",
      "name": "Parse Callback",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "notes": "Parses APPROVE:<jobId> / DENY:<jobId>."
    },
    {
      "parameters": {
        "resource": "callback",
        "operation": "answerQuery",
        "queryId": "={{ $json.callbackId }}",
        "additionalFields": {
          "text": "={{ $json.isApprove ? '\u2705 Approved' : ($json.isDeny ? '\u274c Denied' : '\u26a0\ufe0f Unrecognized') }}",
          "show_alert": false
        }
      },
      "id": "answer-callback",
      "name": "Answer Callback",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        440,
        -140
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "notes": "Must answer within ~10s or Telegram shows a spinner."
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.isApprove }}",
              "value2": true
            }
          ]
        },
        "options": {}
      },
      "id": "if-approve",
      "name": "Approved?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        440,
        120
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.APPROVE_ACTION_URL }}",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { jobId: $json.jobId } }}",
        "options": {}
      },
      "id": "http-approve",
      "name": "Run Approve Action",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        680,
        40
      ],
      "notes": "POSTs { jobId } to APPROVE_ACTION_URL. Put your real action behind this URL."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.DENY_ACTION_URL || 'https://example.com/noop' }}",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { jobId: $json.jobId } }}",
        "options": {}
      },
      "id": "http-deny",
      "name": "Run Deny Action",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        680,
        200
      ],
      "onError": "continueRegularOutput",
      "notes": "Optional. Set DENY_ACTION_URL to record/cleanup on deny; otherwise this no-ops."
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "editMessageText",
        "chatId": "={{ $('Parse Callback').first().json.chatId }}",
        "messageId": "={{ $('Parse Callback').first().json.messageId }}",
        "text": "={{ $('Parse Callback').first().json.messageText + '\\n\\n\u2705 Approved \u2014 ' + $('Parse Callback').first().json.jobId }}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": []
        }
      },
      "id": "edit-approved",
      "name": "Edit Message (Approved)",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        900,
        40
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "notes": "Clears the inline keyboard so the action can't be repeated."
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "editMessageText",
        "chatId": "={{ $('Parse Callback').first().json.chatId }}",
        "messageId": "={{ $('Parse Callback').first().json.messageId }}",
        "text": "={{ $('Parse Callback').first().json.messageText + '\\n\\n\u274c Denied \u2014 ' + $('Parse Callback').first().json.jobId }}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": []
        }
      },
      "id": "edit-denied",
      "name": "Edit Message (Denied)",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        900,
        200
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Parse Callback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Callback": {
      "main": [
        [
          {
            "node": "Answer Callback",
            "type": "main",
            "index": 0
          },
          {
            "node": "Approved?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Approved?": {
      "main": [
        [
          {
            "node": "Run Approve Action",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Run Deny Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Approve Action": {
      "main": [
        [
          {
            "node": "Edit Message (Approved)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Deny Action": {
      "main": [
        [
          {
            "node": "Edit Message (Denied)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false,
  "meta": {
    "templateCredsSetupCompleted": false
  }
}