Build a Telegram bot
The end state: a bot that answers every message with a timestamped echo, running as a deployed app on one of your hosts. This is the full version of the first workflow, including the Telegram side.
1. Create the bot with BotFather
- In Telegram, open a chat with @BotFather.
- Send
/newbot, pick a display name, then a username ending inbot. - BotFather replies with the bot token (
123456789:AA…). Treat it like a password. - Optional but useful:
/setdescription,/setuserpic.
To send yourself messages you’ll also want your chat id: message your new bot once, then
visit https://api.telegram.org/bot<TOKEN>/getUpdates — the message.chat.id field is it.
2. Store the credential
In the Nucleus: Admin → Credentials → New, template Telegram bot. Fill in botToken
(and chatId if you want a default). The token is encrypted at rest; node configs will carry
${credential.telegram-bot.botToken} — a reference, not the value.
3. The workflow
Three nodes — here is the real document the demo ships with (abridged to the parts that matter):
{
"schemaVersion": "niner.workflow.v1",
"name": "Telegram Echo",
"nodes": [
{
"id": "tg-in", "type": "trigger.telegram", "label": "Bot updates",
"config": {
"token": "${credential.telegram-bot.botToken}",
"mode": "polling", "pollTimeoutSeconds": 25
}
},
{
"id": "stamp", "type": "processing.datetime", "label": "Now (NY time)",
"config": {
"operation": "now", "format": "yyyy-MM-dd HH:mm:ss",
"timezone": "America/New_York", "outputField": "receivedAt"
}
},
{
"id": "tg-out", "type": "output.telegram", "label": "Echo reply",
"config": {
"token": "${credential.telegram-bot.botToken}",
"chatId": "${credential.telegram-bot.chatId}",
"operation": "sendMessage",
"text": "⏰ {{ $json.receivedAt }} — You said: {{ $json.message.text }}"
}
}
],
"edges": [
{ "sourceNodeId": "tg-in", "sourcePort": "output", "targetNodeId": "stamp", "targetPort": "input" },
{ "sourceNodeId": "stamp", "sourcePort": "output", "targetNodeId": "tg-out", "targetPort": "input" }
],
"triggers": [{ "nodeId": "tg-in" }]
}
Notes:
- Polling mode needs no public URL — the trigger long-polls the Bot API (25 s timeout per poll). Webhook mode is available when you have a reachable HTTPS endpoint.
- The reply’s
textuses{{ }}expressions directly in the config — no script node. - Replying to the sender (rather than a fixed
chatId) works too:{{ $json.message.chat.id }}.
4. Test in Studio
Test workflow, message the bot, watch the run light up. Open the trigger node’s output to
see the full Telegram update envelope — handy for finding fields to use in expressions
($json.message.from.first_name, $json.message.chat.id, …).
5. Deploy it
Follow deploy to a host: pick a host, deploy in executable mode, and the bot becomes a standalone binary polling Telegram from that machine. The token is not in the artifact — the app leases it from the vault at each run start.
Going further
The Telegram nodes go well beyond echo: the output node covers send/edit/delete/pin, media uploads (photo, document, audio, video — including binary input from upstream nodes), locations, media groups, chat management, callback-query answers and binary-safe file downloads. See the Telegram node reference and Telegram Trigger reference.