Appearance
Telegram integration
Telegram lets your app message users through a Telegram bot: send texts, photos, documents and inline keyboards, edit or delete messages, and react to incoming messages and button taps via webhook triggers.
Use cases
- Send notifications to users or groups (order updates, alerts, reminders)
- Build a support or assistant bot that replies to incoming messages
- Send interactive messages with inline keyboard buttons and react to taps
- Deliver files and images (reports, invoices, media) from backend workflows
Setup
- Open
@BotFatherin Telegram and send the/newbotcommand. - Choose a name and a username for your bot — BotFather replies with an API token.
- In WeWeb, open the
Data & APItab, then theIntegrationssubtab. SelectTelegram, then clickAdd Connection. - Paste the Bot Token and click
Verify— WeWeb confirms which bot the token belongs to. The connection applies to all environments (Editor,Staging,Production); to use different values per environment, override the generated environment variables. - Test by sending a message: open a chat with your bot in Telegram, send it any message (a bot cannot message you first), then use Send Message with your chat ID.
Enabling triggers (webhook)
The two triggers (see below) require the connection's webhook:
- On the connection, set
Telegram WebhooktoEnabledand keep (or adjust) theWebhook Path(default/telegram/webhook). - Save the connection and publish your project — the webhook route only exists after publishing.
- Click
Set webhookon the connection. WeWeb registers the webhook with Telegram, including a secret token used to verify incoming requests.
ONE BOT TOKEN PER ENVIRONMENT
A Telegram bot holds a single webhook URL. Clicking Set webhook from one environment overwrites the webhook of the others and silently kills their triggers. If you use triggers in more than one environment, create a separate bot (and token) per environment.
Triggers
With the webhook enabled and set, two backend triggers become available:
On message received
Runs each time someone sends a message to your bot — e.g. reply with Send Message bound to event.chat.id.
Example event
json
{
"message_id": 2,
"from": { "id": 123456789, "is_bot": false, "first_name": "Jane", "language_code": "en" },
"chat": { "id": 123456789, "first_name": "Jane", "type": "private" },
"date": 1787574749,
"text": "Hello"
}On button tapped
Runs when someone taps an inline keyboard button (with callback_data) sent by your bot. Answer it with Answer Callback Query bound to event.id — until answered, the user sees a loading spinner on the button. Branch on event.data (the button's callback_data) to route different buttons in one workflow.
Example event
json
{
"id": "4382156871125391245",
"from": { "id": 123456789, "is_bot": false, "first_name": "Jane", "language_code": "en" },
"message": {
"message_id": 5,
"chat": { "id": 123456789, "first_name": "Jane", "type": "private" },
"date": 1787574749,
"text": "Pick an option"
},
"data": "confirm"
}Common pitfalls (setup & usage)
Bots cannot message a user first
Sending to a user who never started your bot fails with 403 bot can't initiate conversation with a user, and an unknown chat ID fails with 400 chat not found. The user must have started the bot (or share a group with it). Chat IDs usually come from trigger events (event.chat.id) — there is no API to list a bot's chats.
MarkdownV2 formatting errors
MarkdownV2 requires escaping special characters (., !, -, (, ), #, etc.) with \, and unescaped characters fail with a 400 error — the most common send failure. For dynamic content, prefer HTML formatting (<b>, <i>, <a href>) or plain text.
Media URL fails to load
Telegram fetches photo and document URLs server-side. Auth-gated links or URLs that return an HTML page fail with 400 failed to get HTTP URL content. Use a publicly accessible direct file URL (max 5 MB for photos, 20 MB for documents by URL), or reuse the file_id of an already-sent file.
Group privacy mode
By default, a bot in a group only receives commands, replies to its own messages, and messages that mention it. To receive all group messages, disable privacy mode via @BotFather with /setprivacy.
Callback queries expire in seconds
Answer every On button tapped trigger with Answer Callback Query first, then do slower work. Late answers fail with 400 query is too old, and unanswered buttons show a spinner to the user for ~30 seconds.
All Actions
This integration provides seven actions mapped to the Telegram Bot API.
| Action | Description |
|---|---|
| Send Message | Send a text message, optionally formatted and with an inline keyboard |
| Send Photo | Send a photo by public URL or Telegram file_id |
| Send Document | Send a file of any type by public URL or Telegram file_id |
| Edit Message Text | Edit the text (and inline keyboard) of a message the bot sent |
| Delete Message | Delete a message |
| Get Chat | Get up-to-date information about a chat |
| Answer Callback Query | Answer an inline keyboard button tap |
Action details
Send Message
Send a text message from the bot to a chat, optionally formatted (MarkdownV2/HTML) and with an inline keyboard.
Inputs
| Display Key | Example Input | Description | Restrictions |
|---|---|---|---|
Chat ID | "123456789" | Unique identifier of the target chat, or a channel username (@channelusername). Usually bound from a trigger event (event.chat.id). | Required; the user must have started the bot |
Text | "Your order has shipped!" | Text of the message. | 1–4096 characters after parsing |
FormattingOptional | "HTML" | How Telegram parses the text. Empty = plain text. | Valid: MarkdownV2, HTML |
Reply to message IDOptional | 42 | Send this message as a reply to another message in the same chat. | Number |
Inline keyboardOptional | {"inline_keyboard":[[{"text":"Confirm","callback_data":"confirm"}]]} | Raw InlineKeyboardMarkup object: rows of buttons, each with a text and a url or callback_data. Tapping a callback_data button fires the On button tapped trigger. | Valid InlineKeyboardMarkup object |
Send silentlyOptional | false | Users receive the message without a notification sound. | Boolean |
Protect contentOptional | false | Prevents the message from being forwarded or saved. | Boolean |
Example output
json
{
"message_id": 2,
"chat": { "id": 123456789, "type": "private" },
"date": 1787574749,
"text": "Your order has shipped!"
}Documentation of API endpoint that powers action: Telegram Bot API – sendMessage (POST /sendMessage)
Send Photo
Send a photo from the bot to a chat, by public URL or Telegram file_id.
Inputs
| Display Key | Example Input | Description | Restrictions |
|---|---|---|---|
Chat ID | "123456789" | Unique identifier of the target chat, or a channel username. | Required |
Photo | "https://example.com/image.jpg" | A publicly accessible image URL (Telegram fetches it server-side) or a Telegram file_id of an already-sent photo. | Max 5 MB by URL |
CaptionOptional | "Our new office!" | Photo caption. | 0–1024 characters after parsing |
FormattingOptional | "HTML" | How Telegram parses the caption. Empty = plain text. | Valid: MarkdownV2, HTML |
Reply to message IDOptional | 42 | Send as a reply to another message in the same chat. | Number |
Inline keyboardOptional | {"inline_keyboard":[[{"text":"View","url":"https://example.com"}]]} | Raw InlineKeyboardMarkup object. | Valid object |
Send silentlyOptional | false | No notification sound. | Boolean |
Protect contentOptional | false | Prevents forwarding or saving. | Boolean |
Example output
json
{
"message_id": 3,
"chat": { "id": 123456789, "type": "private" },
"date": 1787574749,
"photo": [ { "file_id": "AgADAgADwqcxGy…", "width": 320, "height": 320 } ],
"caption": "Our new office!"
}Documentation of API endpoint that powers action: Telegram Bot API – sendPhoto (POST /sendPhoto)
Send Document
Send a file (any type) from the bot to a chat, by public URL or Telegram file_id.
Inputs
| Display Key | Example Input | Description | Restrictions |
|---|---|---|---|
Chat ID | "123456789" | Unique identifier of the target chat, or a channel username. | Required |
Document | "https://example.com/report.pdf" | A publicly accessible file URL (Telegram fetches it server-side) or a Telegram file_id of an already-sent file. | Max 20 MB by URL |
CaptionOptional | "Monthly report attached" | Document caption. | 0–1024 characters after parsing |
FormattingOptional | "HTML" | How Telegram parses the caption. Empty = plain text. | Valid: MarkdownV2, HTML |
Reply to message IDOptional | 42 | Send as a reply to another message in the same chat. | Number |
Inline keyboardOptional | {"inline_keyboard":[[{"text":"Open","url":"https://example.com"}]]} | Raw InlineKeyboardMarkup object. | Valid object |
Send silentlyOptional | false | No notification sound. | Boolean |
Protect contentOptional | false | Prevents forwarding or saving. | Boolean |
Example output
json
{
"message_id": 4,
"chat": { "id": 123456789, "type": "private" },
"date": 1787574749,
"document": { "file_id": "BQADAgADy6cxGy…", "file_name": "report.pdf", "mime_type": "application/pdf" },
"caption": "Monthly report attached"
}Documentation of API endpoint that powers action: Telegram Bot API – sendDocument (POST /sendDocument)
Edit Message Text
Edit the text (and optionally the inline keyboard) of a message previously sent by the bot.
Inputs
| Display Key | Example Input | Description | Restrictions |
|---|---|---|---|
Chat ID | "123456789" | The chat containing the message. | Required |
Message ID | 42 | Identifier of the target message (message_id from a sent or received message). | Required |
New text | "Order status: shipped ✅" | New text of the message. | 1–4096 characters after parsing |
FormattingOptional | "HTML" | How Telegram parses the new text. Empty = plain text. | Valid: MarkdownV2, HTML |
Inline keyboardOptional | {"inline_keyboard":[[{"text":"Track","url":"https://example.com"}]]} | Replace the message's inline keyboard. | Valid object |
Editing a message with identical content fails with 400 message is not modified — guard workflows that might re-apply the same edit.
Example output
json
{
"message_id": 42,
"chat": { "id": 123456789, "type": "private" },
"date": 1787574749,
"text": "Order status: shipped ✅"
}Documentation of API endpoint that powers action: Telegram Bot API – editMessageText (POST /editMessageText)
Delete Message
Delete a message. Only works within 48 hours of the message being sent (including the bot's own messages); deleting others' messages additionally requires the bot to be a group admin.
Inputs
| Display Key | Example Input | Description | Restrictions |
|---|---|---|---|
Chat ID | "123456789" | The chat containing the message. | Required |
Message ID | 42 | Identifier of the message to delete. | Required; within 48h of sending |
Example output
json
trueDocumentation of API endpoint that powers action: Telegram Bot API – deleteMessage (POST /deleteMessage)
Get Chat
Get up-to-date information about a chat (private chat, group, supergroup or channel).
Inputs
| Display Key | Example Input | Description | Restrictions |
|---|---|---|---|
Chat ID | "123456789" | Unique identifier of the chat, or a channel username. | Required |
Example output
json
{
"id": 123456789,
"type": "private",
"first_name": "Jane",
"username": "janed"
}Documentation of API endpoint that powers action: Telegram Bot API – getChat (POST /getChat)
Answer Callback Query
Answer a callback query from an inline keyboard button tap. Call it after every On button tapped trigger — until answered, the user sees a loading spinner on the button.
Inputs
| Display Key | Example Input | Description | Restrictions |
|---|---|---|---|
Callback query ID | "4382156871125391245" | Identifier of the query to answer — bind event.id from the On button tapped trigger. | Required; queries expire within seconds |
Notification textOptional | "Done!" | Text shown to the user as a notification at the top of the chat. Empty = just stop the spinner. | 0–200 characters |
Show as alertOptional | false | Show the text as a modal alert instead of a passing notification. | Boolean |
Cache time (seconds)Optional | 0 | Maximum time Telegram clients may cache this answer, in seconds. | Number |
Example output
json
trueDocumentation of API endpoint that powers action: Telegram Bot API – answerCallbackQuery (POST /answerCallbackQuery)
Error handling
| Error code and type | Reason |
|---|---|
| 400 Bad Request | Unknown chat (chat not found), unreachable media URL (failed to get HTTP URL content), unescaped MarkdownV2 characters, identical edit (message is not modified), or late callback answer (query is too old). |
| 401 Unauthorized | Invalid bot token. |
| 403 Forbidden | The user never started the bot (bot can't initiate conversation with a user), or the bot was blocked or lacks group permissions. |
| 429 Too Many Requests | Rate limited — the response includes parameters.retry_after (seconds to wait). Telegram allows roughly 1 message/second per chat, 20/minute per group, and ~30/second overall. |
FAQs
How do I find my chat ID?
Enable the webhook, send a direct message to your bot, and read event.chat.id from the On message received trigger event. There is no API to list a bot's chats.
Why don't my triggers fire?
Check, in order: the webhook is Enabled on the connection, the project has been published (the webhook route only exists after publishing), and you clicked Set webhook afterwards. Also note a bot has a single webhook URL — setting the webhook from another environment overwrites it.
Should I use MarkdownV2 or HTML formatting?
For dynamic content, prefer HTML (<b>, <i>, <a href>): MarkdownV2 requires escaping many special characters with \ and unescaped input fails with a 400 error. If you don't need styling, leave Formatting empty for plain text.
Why doesn't my bot see group messages?
Group privacy mode is on by default: the bot only receives commands, replies to its own messages, and mentions. Disable privacy via @BotFather with /setprivacy to receive all group messages.
How do I send interactive buttons and react to taps?
Send a message with an Inline keyboard whose buttons carry callback_data. When a user taps one, the On button tapped trigger fires with the button's callback_data in event.data — answer it with Answer Callback Query (bound to event.id) first, then run your logic.
Can I upload a file directly from a workflow?
No — media is sent by public URL (or by reusing a Telegram file_id from a previously sent file). Binary upload from workflows is not supported.

