Add Telegram
The Telegram adapter talks directly to the Telegram Bot API. It uses bot-wide getUpdates long polling, which has one consequence worth knowing before anything else: one bot token must have exactly one active poll owner.
Read Connect a chat app to Virgo first. Registering an adapter on an installation you already run is supported through adapter add, so the steps below end at that command rather than at a descriptor you can only prepare.
Before you start
- A Virgo installation you already run, with its Hub reachable.
- A Telegram bot and its token, created through Telegram's own documentation. Virgo does not create bots for you.
- The numeric chat IDs you intend to allow, and the numeric user IDs of the people who will approve new senders.
Because polling is bot-wide, do not point a second process — another Virgo installation, a local script, a hosted service — at the same token. Two pollers race for the same updates.
Store the bot token privately
credential is a reference resolved at boot. The configuration never contains a token.
The descriptor you will need
Placeholders only.
{
"id": "telegram-main",
"kind": "telegram",
"session": "telegram-session",
"vsp": "vsp:/your-account:your-space/telegram/bot",
"credential": "credential:telegram/main",
"targetVsp": "vsp:/your-account:your-space/your-repo/lead",
"defaultChatId": 100000001,
"accessNotificationChatId": 100000001,
"allowedChatIds": "all",
"artifactPort": "adapter-port:artifacts/main",
"access": {
"administrators": ["telegram.user:100000002"]
}
}
targetVsp is the canonical destination for accepted inbound messages. defaultChatId is optional and becomes the default outbound conversation. allowedChatIds is either "all" or a non-empty unique array of numeric chat IDs; at most 10,000 unique non-zero safe integers. Use "all" for approval-mode discovery of new private chats.
accessNotificationChatId is where pending approval requests are announced. It is required whenever access is in approval mode — which is the default — and installation refuses the descriptor without it. It must be a chat the scope allows, and when defaultChatId is also set the two must be the same chat.
Whichever you choose, the adapter binds every inbound event and outbound command to its actual telegram.chat:<id> conversation and rejects cross-chat message references.
Access and administrators
Telegram defaults to approval. That default is why the descriptor must name at least one stable administrator principal such as telegram.user:100000002. Usernames and display names never authenticate a sender; a numeric user ID is stable and a username is not.
An administrator decides a pending request with /virgo-access approve <requestId> or /virgo-access deny <requestId>. Requests are deduplicated by provider, channel, tenant and canonical principal, so several private conversations from one person join a single request while keeping their own conversation and message context. An administrator may decide a request from a different conversation on the same configured channel.
A sender that is a channel rather than a person appears separately as telegram.chat:<id> with sender kind chat.
Registering it on an installation you already have
install accepts the descriptor through --adapters with --host-settings when you are building a new installation. When the installation already exists, the supported route is adapter add:
adapter add --file /absolute/path/to/telegram.json
adapter list
adapter remove telegram-main
The path must be absolute. An id already in the roster is refused as a conflict rather than replaced, only the new entry is prepared, and the whole roster is then validated the way a boot validates it — so an add cannot leave a configuration that will not start. A credentialBinding in the descriptor is registered in the same operation rather than as a separate step. adapter list reads the roster back with the credential reference each entry names, never the secret itself.
It becomes active on the next Host start. A running Host keeps the adapters it booted with, and native agent sessions are left running.
Do not reinstall your Hub to add Telegram.
What the adapter can and cannot do
Supported operations are ordinary messages, quoted replies, typing-on, editing messages sent by the current adapter process, emoji reactions and optional document upload.
Telegram has no stop-typing operation. It does not support the shared Adaptive, Hero or Thumbnail card formats. It has no effect-key idempotency and no general message lookup, so a lost or ambiguous response is recorded as unknown and is never submitted again by that adapter instance.
Text is limited to 4,096 Unicode code points. Poll timeouts are 1 to 50 seconds.
Files
Attachments work in both directions once an artifact port resolves. Outbound, the adapter verifies the declared byte length and SHA-256 before sending a document. Inbound, it fetches the file from Telegram and stores it through the same port.
artifactPort is optional in the descriptor because the installation falls back to the managed artifact port when it is absent. If no artifact reader resolves at all, basic messaging still boots, outbound attachments report not_implemented, and an inbound attachment is refused as not configured rather than silently dropped.
Troubleshooting and removal
- Updates stop or arrive twice. Almost always two poll owners on one token. Ensure exactly one process polls it.
- A message is accepted but nothing replies. Check whether the sender is pending an administrator decision;
approvalis the default. - A document is not sent, or an inbound file is refused. Confirm an artifact port resolves. The installation falls back to the managed port, so this usually means that fallback did not resolve either.
- Recovery. The durable cursor advances only when an inbound delivery is acknowledged, so an unacknowledged update is delivered again after recovery.
- Removal. Revoke the token with BotFather and retire the credential.
adapter remove <id>takes it out of an existing installation's configuration, and the Host stops serving it at the next start.
What is verified today
The adapter source is integrated and its configuration validates. That is not live bot acceptance on your account. Registration on an existing installation has been exercised on a canonical route for Discord only, and it reached Gateway READY there; an agent claiming its Seat, a human inbound message and a correlated reply are unaccepted on every provider, Telegram included. See Availability and evidence.
Protocol reference: Telegram Bot API.