Set up Telegram

Telegram needs one Bot API token for triggers, messages, approvals, and lifecycle notifications. It does not need an app token or a public callback URL; WOML receives messages through long polling.

Create the bot

  1. Open Telegram and start a conversation with the verified @BotFather account.
  2. Send /newbot.
  3. Choose a display name.
  4. Choose a unique username ending in bot, such as my_woml_bot.
  5. Copy the Bot API token returned by BotFather. Treat it like a password.

Store and verify the token

Terminal
woml secrets set TELEGRAM_BOT_TOKEN
woml telegram doctor

The doctor command verifies that Telegram accepts the token without exposing it in workflow source or terminal output.

Test a Telegram trigger and reply

Save this as telegram-test.woml:

WOML
<woml>
  <workflow
    id="telegram-test"
    name="Telegram Test"
    description="Replies to every message received by the bot."
    version="1.0.0"
  >
    <triggers>
      <telegram
        id="messageReceived"
        events="message"
        bot-token="{{secrets.TELEGRAM_BOT_TOKEN}}"
      />
    </triggers>

    <steps>
      <step id="reply" name="Reply in Telegram">
        <script>
          return services.telegram.send({
            botToken: secrets.TELEGRAM_BOT_TOKEN,
            conversationId: context.payload.conversationId,
            replyToMessageId: context.payload.messageId,
            text: `WOML received: ${context.payload.text}`
          }, { name: "telegram-test-reply" });
        </script>
      </step>
    </steps>
  </workflow>
</woml>

Run it:

Terminal
woml check telegram-test.woml
woml run telegram-test.woml

Open the bot in Telegram, press Start or send /start, then send hello. WOML should accept the message and the bot should reply with WOML received: hello.

The run output includes the normalized conversationId. Use that numeric value as a notification destination. You can verify it before using approvals:

Terminal
woml telegram doctor --destination <conversationId>

An approval notification then looks like this:

WOML
<approval id="review" timeout="24h" on-timeout="reject">
  <notify>
    <telegram
      chats="<conversationId>"
      bot-token="{{secrets.TELEGRAM_BOT_TOKEN}}"
    />
  </notify>
  <when-approved />
  <when-rejected />
</approval>

For groups and channels, the ID is commonly negative. Copy the exact value; do not remove the minus sign.

Configure group messages when needed

Telegram bots use privacy mode in groups by default. In that mode they receive commands, replies directed to them, and other explicitly relevant messages, but not every ordinary group message. If the workflow genuinely needs all group messages:

  1. Open BotFather and send /setprivacy.
  2. Select the bot and disable privacy mode.
  3. Remove and re-add the bot to the group so Telegram applies the change.

Only disable privacy when the automation requires it; narrower access is safer and less noisy.

Diagnose Telegram setup failures

  • Unauthorized: replace TELEGRAM_BOT_TOKEN with the current BotFather token.
  • Nothing happens in a private chat: press Start first; bots cannot begin a conversation with a user.
  • The trigger repeatedly rejects the same update: stop other processes using the same bot token. Only one long-polling consumer should own that bot.
  • Group messages are missing: mention/reply to the bot or review privacy mode.
  • Notification delivery fails: verify the numeric chat ID with woml telegram doctor --destination ... and make sure the bot is still a member of the group or channel.