One stuck approval used to freeze everything behind it. The approver kept refreshing their inbox, the requester kept asking "has it been approved yet?" The problem wasn't the approval itself — it was the time spent waiting for it.
So we pulled approvals out of the inbox. When a request comes in, a card flies to the approver's Telegram, and one button tap runs the next step of the workflow immediately.
How it works
The workflow is two trigger chains. The first sends the approval card to Telegram. The second polls for the approver's button tap and pushes the result back downstream. State lives briefly in n8n's workflow static data and is read back out when the callback arrives.
Send chain
- A Webhook node receives the approval request as an HTTP POST.
- An Edit Fields (Set) node normalizes the payload (requestId, title, amount, requester, approverChatId, callbackUrl).
- A Code node saves
{ requestId: callbackUrl }into workflow static data. - A Telegram Send Message node posts the approval card to the approver's chat. The card has two inline buttons whose
callback_dataisapprove:REQ-IDorreject:REQ-ID. - A Respond to Webhook node immediately returns
{"status":"pending","requestId":"..."}to the requester.
Callback chain
- A Telegram Trigger node polls for
callback_queryupdates. Because nothing needs to reach n8n from the outside, this works as-is in a local dev environment. - A Code node parses
decisionandrequestIdfromcallback_data, looks up the storedcallbackUrl, and removes it from static data. - An HTTP Request node POSTs
{decision, requestId}to thecallbackUrlthe requester supplied. - A Telegram: Answer Callback Query node clears the button's loading spinner and shows a done toast to the user.
What you need
- n8n 2.29.9 or later (self-hosted or Cloud). Verified on self-hosted Docker (
docker.n8n.io/n8nio/n8n). - Core nodes only (Webhook · Set · Code · Telegram Send Message · Telegram Trigger · HTTP Request · Respond to Webhook · Telegram Answer Callback Query). No community nodes.
- A Telegram Bot token from BotFather — register it in n8n Credentials as
Telegram API. - The approver's numeric chat ID — message
@userinfobotand it will tell you.@usernamewill not work. - The approver must have sent the bot at least one message (Telegram policy). Search for the bot and hit
Startonce. - A downstream endpoint to receive the result (
callbackUrl). A freewebhook.siteURL is enough for testing. - The send chain and the callback chain must live in the same workflow. They share state via workflow static data (
$getWorkflowStaticData('global')). - A bad chat ID causes the Telegram Send Message node to fail with
Bad Request: chat not found. In production, attach alerting or a retry branch here. - Static data is volatile — it vanishes when the workflow is deactivated or deleted. For long-running approvals or audit trails, swap it for a durable store like Postgres.
Sample payload
curl -X POST http://localhost:5678/webhook/telegram-approval \
-H "Content-Type: application/json" \
-d '{
"requestId": "REQ-2026-001",
"title": "Budget approval — Q3 marketing",
"amount": 1500000,
"requester": "Jihoon Kim",
"approverChatId": "8104222459",
"callbackUrl": "https://webhook.site/your-uuid"
}'I clear all my approvals on my phone during the commute. By the time I get to the office, the next step is already running.— Operations manager