How to Add Human Approval to AI Agent Workflows with n8n and Telegram
An AI workflow can draft an X post in seconds. If it publishes the wrong one without asking, that time saving disappears quickly.
A better starting point is to let the workflow prepare the work and ask you before it sends, publishes, edits, or deletes anything outside n8n. In this guide, that confirmation happens in Telegram:
Trigger → AI generates a draft → Telegram approval request
↓
Approve → perform the action
Reject → stop and record the decision
The same pattern works for customer emails, local files, calendar events, and other actions that have a consequence outside the workflow. The example below uses an X-ready draft because it is easy to test without immediately giving the workflow permission to publish.
Quick Decision Guide
| If the workflow wants to… | Approval recommended? | Why |
|---|---|---|
| Classify an email or summarize a document | Usually no | The result is easy to review later and has no external side effect |
| Draft an X post or newsletter | Yes | The model can invent, omit, or misinterpret a claim |
| Send an email to a customer | Yes | The message represents you or your organization |
| Edit or delete a local file | Yes | The action can be difficult to undo |
| Create a low-risk internal record | Maybe | Skip approval only if the operation is reversible and monitored |
| Make a payment or change account permissions | Always | Treat it as a high-risk action with explicit confirmation |
The useful question is: “What happens if this particular output is wrong?” If the answer is “someone sees it,” “a customer receives it,” or “data changes,” add a review step.
What You’ll Build
The finished workflow has five logical stages:
- Trigger — receive a URL, email, form submission, or scheduled item.
- Generate — ask an AI model to create a draft or proposed action.
- Request approval — send the draft to Telegram with clear choices.
- Wait for a decision — pause the workflow until the reviewer approves or rejects it.
- Execute or stop — perform the external action only after approval.
For this example, the workflow turns a webpage into an X-ready post and sends the draft to Telegram. The X publishing node stays after the approval check, so it cannot run while the draft is still waiting for review.
This is a review-first version of the webpage-to-X workflow. That article covers the content-reformatting part; this one focuses on the decision that should happen before publishing.
What You’ll Need
- An n8n instance. If you have not installed it yet, use How to Install n8n: 3 Ways.
- A Telegram account and a Telegram bot created through BotFather.
- An AI provider credential configured in n8n.
- A destination account or API credential for the action you want to protect. For X publishing, confirm the current API access and pricing before committing.
- A test input that is safe to process. Do not begin with real customer emails or production publishing.
You can use a hosted n8n instance or self-host it. Only add the VPS deployment guide if this workflow will run on a schedule or wait for approvals around the clock; it covers process supervision and restart behavior.
Step 1: Create the Telegram Bot
Open Telegram and start a conversation with BotFather. Create a bot and copy the token it gives you.
Do not paste the token into an AI prompt, a Code node, or a public workflow export. Store it in n8n credentials instead. Anyone with the token can operate the bot.
Next, send one message to your new bot from the Telegram account that should receive approval requests. A bot cannot normally start a conversation with a user who has never contacted it.
You need the destination chat ID for the Telegram node. Depending on your n8n version and Telegram credential setup, you can obtain it through the Telegram node's test output or by using Telegram's update endpoint during initial setup. Keep the chat ID in the workflow configuration, not in the generated AI text.
Before continuing, send a simple test message from n8n to Telegram. If it does not arrive, fix the bot credential and chat ID first. Do not debug the AI portion and the Telegram portion at the same time.
Step 2: Build the Drafting Part
The first part of the workflow can use any trigger. For a webpage-to-post example, use:
Manual Trigger or Webhook
→ HTTP Request
→ HTML extraction or text cleanup
→ AI model
The AI node should produce a structured draft, not a block of prose that later nodes need to interpret. A simple output shape is:
{
"source_url": "https://example.com/article",
"draft_text": "The proposed post goes here.",
"reasoning_summary": "One sentence explaining the main claim.",
"risk_notes": [
"Verify the number in the second sentence before publishing."
],
"status": "pending_approval"
}
The exact field names are up to you. The important part is that later nodes receive predictable fields. Also keep the approval state outside the model response: a model can propose a draft, but it cannot approve its own work.
Use a prompt with explicit boundaries:
You prepare a draft for human review.
Rules:
- Do not publish, send, delete, edit, or call an external action.
- Preserve names, dates, numbers, and source URLs from the input.
- If a claim cannot be verified from the input, put it in risk_notes.
- Return valid JSON only.
- Set status to "pending_approval".
Return:
{
"source_url": "string",
"draft_text": "string",
"reasoning_summary": "string",
"risk_notes": ["string"],
"status": "pending_approval"
}
The prompt helps the model stay within scope, but it is not a security control. The real control is the workflow layout: the publishing node must be reachable only after a verified approval result.
For model selection, start with the least expensive model that produces an acceptable draft. Our guide to reducing AI Agent API costs covers context size, caching, model choice, and monitoring.
Step 3: Format a Review Message
After the AI node, add a Telegram node that sends the draft to the reviewer. Keep the message short enough to review on a phone.
A useful format is:
📝 Approval required
Source:
{{ $json.source_url }}
Draft:
{{ $json.draft_text }}
Why this draft:
{{ $json.reasoning_summary }}
Risk notes:
{{ $json.risk_notes.join('\n- ') }}
Review the source before approving.
Do not display only the generated text. The reviewer needs enough context to make a quick decision:
- What source produced the draft?
- What action will happen after approval?
- What uncertainties did the model identify?
- Is the action reversible?
If the message can become long, send the source URL separately or attach the full draft as a file. Avoid truncating the actual content without telling the reviewer that it was truncated.
Step 4: Add Approve and Reject Controls
The approval request needs two clear outcomes:
- Approve — continue to the external action.
- Reject — stop the workflow and record the reason if available.
The exact Telegram interaction depends on the n8n Telegram node and version you are using. One common setup uses an inline keyboard with callback data such as:
[
[
{"text": "✅ Approve", "callback_data": "approve"},
{"text": "❌ Reject", "callback_data": "reject"}
]
]
Use the keyboard or reply-markup fields shown by your installed n8n version rather than copying a parameter name from an older tutorial. Test one button first and inspect the Telegram update that n8n receives before building the routing logic.
The callback data should contain an action, not the entire draft. Keep the draft in n8n's execution data or a separate short-lived store, and pass a stable identifier through the approval message. For example:
approval_id = article-2026-07-31-001
callback_data = approve:article-2026-07-31-001
This matters when several requests are waiting at once: the callback identifies the exact draft being reviewed.
Step 5: Wait for the Decision
There are two practical ways to pause for approval.
Option A: Use a Wait or Form-Based Pattern
Use n8n's current Wait or human-in-the-loop capability if it supports the interaction you need. The workflow pauses, stores its state, and resumes when the approval request is completed.
Use this when your n8n version supports the interaction you need. The waiting state remains attached to the workflow execution instead of being managed separately.
Option B: Split the Request and Callback into Two Workflows
For a more explicit design, use two workflows:
Workflow A: Create approval request
Trigger
→ Generate draft
→ Create approval_id
→ Store draft and status=pending
→ Send Telegram message
Workflow B: Process Telegram callback
Telegram callback trigger
→ Parse approval_id and action
→ Load stored approval record
→ Verify reviewer
→ Check status is still pending
→ Approve: execute action
→ Reject: mark rejected
The two-workflow design is useful when an approval may remain open for hours or when you need a record that survives beyond one execution. It also makes the permission and duplicate-run checks easier to inspect.
A small database table or data store can contain:
| Field | Purpose |
|---|---|
approval_id |
Links the Telegram decision to the original draft |
status |
pending, approved, rejected, or expired |
draft_hash |
A content fingerprint used to detect whether the draft changed after review |
created_at |
Supports expiration and audit history |
reviewed_at |
Records when the decision happened |
reviewer_id |
Restricts approval to the intended person or group |
action_target |
Identifies what will be changed or published |
Here, a hash is simply a short fingerprint string generated from the draft's content. If the draft changes after the Telegram message is sent, its fingerprint changes too, and the workflow can ask for approval again instead of using an old decision.
Do not store only a Boolean such as approved=true. It does not tell you which draft was approved, who approved it, or whether the action already ran.
Step 6: Verify the Approval Before the Side Effect
This check is what separates a review request from an actual approval gate.
Before the publishing, sending, editing, or deletion node, verify all of the following:
- The action is exactly
approve. - The
approval_idexists. - The approval record is still
pending. - The callback came from the expected Telegram chat or user.
- The draft hash still matches the version that was reviewed.
- The approval has not expired.
- The action has not already been executed.
Only after these checks should the workflow reach the side-effect node.
A practical routing shape is:
Telegram callback
→ Parse action and approval_id
→ Load approval record
→ IF reviewer is authorized
→ IF status is pending
→ IF draft hash matches
→ IF action is approve
├─ yes → execute external action
└─ no → mark rejected or expired
Keep these checks visible as separate IF nodes. When a run fails, you can see whether the problem was the reviewer, the status, the draft, or the action itself.
Step 7: Prevent the Same Approval from Running Twice (Idempotency)
An approval callback can be delivered twice. A reviewer can tap a button twice. A network retry can cause the same request to be processed again.
The workflow should treat repeated approval events as harmless.
The basic rule is:
The first valid approval changes the record from
pendingtoapproved. Every later callback sees a non-pending status and stops without executing the action again.
For a publish operation, also store the result from the destination service:
{
"approval_id": "article-2026-07-31-001",
"status": "executed",
"external_id": "destination-returned-id",
"executed_at": "2026-07-31T12:00:00Z"
}
If the external service returns an ID, save it. If the same workflow is retried later, check for that ID before creating another post or message.
This is especially important for the webpage-to-X workflow, where a duplicated callback could otherwise publish the same content twice.
Step 8: Handle Rejection, Expiration, and Errors
A usable approval workflow also needs clear behavior for rejection, expiration, and failure.
Rejection
When the reviewer selects Reject:
- Mark the approval record as
rejected. - Do not call the external action node.
- Send a confirmation to Telegram.
- Keep the original draft and source for debugging.
You can add a second interaction that asks for a rejection reason, but do not make the reason a requirement if that slows down quick decisions. A simple rejection with an optional note is often enough.
Expiration
An approval should not remain valid forever. Add an expiry time, such as 24 hours for a social post or a shorter window for time-sensitive operations.
An expired request should be treated as rejected, not automatically approved. If the source changes or the context becomes stale, generate a new draft and request approval again.
Workflow errors
If the final action fails after approval, do not automatically send the item back through the AI node. Preserve the approved draft and mark the action as execution_failed.
Then notify the reviewer with:
- The approval ID
- The action that failed
- The external error message, with secrets removed
- Whether a retry is safe
- A link to the n8n execution if appropriate
For general authentication failures in n8n and AI tools, see AI Agent Not Responding? API Key & Auth Errors. For long-running workflows, remember that automatic restarts do not fix a permanently invalid credential.
Security Checks You Should Not Skip
Human approval reduces risk, but it does not replace basic security controls.
Restrict who can approve
Check the Telegram chat ID and, where available, the Telegram user ID. A callback with the right text is not enough. A person outside the intended chat should not be able to approve an action just by guessing an ID.
Keep secrets out of workflow data
Use n8n credentials for API keys and bot tokens. Do not include credentials in prompts, Telegram messages, JSON exports, or error notifications.
Limit the action scope
An approval should authorize one specific action, not a broad capability. “Publish this draft to this account” is safer than “let the agent publish anything.”
Avoid approving stale content
Include the source URL, a timestamp, and a draft hash in the approval record. If the content changes after the message is sent, invalidate the approval and request a new review.
Separate drafting from execution
The AI node may propose a command or API request, but it should not be able to bypass the approval branch. Keep external side effects in nodes that are only reachable after the verification checks.
For file-related workflows, this works alongside the guide to connecting AI agents to local files: limit access to the required directory, and require confirmation before write or delete operations.
Testing Checklist
Test the workflow with fake data before connecting real accounts or production destinations.
| Test | Expected result |
|---|---|
| Approve a valid pending draft | The action runs once |
| Tap Approve twice | The second callback does nothing |
| Reject a draft | No external action runs |
Use an unknown approval_id |
The workflow stops and logs the event |
| Approve from an unauthorized chat | The workflow rejects the callback |
| Approve after expiration | The workflow marks it expired |
| Change the draft after sending the request | The hash check fails |
| Retry a failed execution | No duplicate side effect occurs |
| Send a malformed callback | The workflow stops safely |
| Remove or break an API credential | The failure is reported without exposing secrets |
Use n8n's execution history to inspect each branch. A single successful approval is not enough; test rejection, duplicate clicks, expiry, and failed credentials too.
Common Mistakes
Putting the publish node before the approval branch
The node order matters. If the publish node is connected directly to the AI output, it can run before Telegram receives the request.
Letting the model produce the approval decision
The AI can flag risk or recommend review, but it should not be the final authority for an external action. The decision must come from the authorized reviewer.
Using the draft text as the identifier
Draft text can change, contain special characters, or be duplicated. Use a generated approval ID instead.
Sending every detail to Telegram
A review message should contain enough information to decide, not the entire execution payload. Keep logs and sensitive metadata in n8n or a protected data store.
Treating a restart policy as a safety feature
A restart policy keeps a process alive; it does not validate approvals or prevent duplicate actions. If you run the workflow continuously, combine supervision with execution logging and health checks. The 24/7 AI Agent guide covers that distinction in more detail.
Bottom Line
Human approval gives an AI workflow a clear boundary: the model prepares a proposal, and a person decides whether anything happens outside the workflow.
Let the model read the input, extract the important details, draft the message, and flag uncertainty. Keep publishing, sending, editing, and deleting behind the approval check.
Start with one reversible action, such as approving an X draft or sending a private Telegram notification. Add an approval ID, an expiration time, an authorized reviewer check, and an idempotency guard before connecting the workflow to real data.
You do not need to review every internal step. Ask for confirmation at the point where a mistake would reach a customer, publish publicly, or change data.
Further Reading
- From Any Webpage to X-Ready Posts — the drafting and publishing workflow used as the example in this guide.
- How to Install n8n: 3 Ways — choose a local, cloud, or self-hosted setup.
- How to Run AI Agents 24/7 on a VPS — keep long-running workflows supervised after the test succeeds.