Skip to main content
Build a fully functional Telegram bot that allows users to interact with their Binance accounts through natural language. The bot uses an AI agent to understand user requests and execute trading operations on their behalf — all configured through the ServFlow dashboard without writing code.
What you’ll build:
  • A Telegram bot that responds to messages via webhook
  • Secure per-user credential storage in MongoDB
  • AI-powered natural language processing for trading commands
  • Dynamic Binance integration using stored credentials
  • Account balance checking and price history tools
Time to complete: ~15 minutes

Quick Import

Want to skip ahead? Import the complete workflow configuration directly:
  1. Download the configuration from the ServFlow Cookbook
  2. In the ServFlow dashboard, click Import in the Files panel
  3. Select the downloaded YAML file
  4. Configure your integrations (MongoDB, OpenAI, Binance)
  5. Add your TELEGRAM_BOT_TOKEN secret
The rest of this tutorial explains each component in detail.

Architecture Overview

Before building, let’s understand how the bot processes messages:
Telegram bot workflow flowchart showing message processing logic
The workflow follows this decision tree:
  1. New message arrives → Check if user has stored Binance credentials
  2. If credentials exist → Send typing indicator → Process with AI agent → Send response
  3. If no credentials → Check if message is a /save_creds command
  4. If save command → Store credentials → Confirm to user
  5. If not save command → Tell user how to save credentials

Prerequisites

Before starting, ensure you have:
1

ServFlow Pro Running

ServFlow Pro with the dashboard enabled. See Installation if needed.
2

Telegram Bot Token

Create a bot via BotFather and save your bot token.
3

MongoDB Integration

A configured MongoDB integration named mongo-main. See Configuration Reference.
4

OpenAI Integration

A configured OpenAI integration named open-ai-main.
5

Binance API Credentials (Optional)

For testing trades, you’ll need Binance API keys. The bot stores user credentials, so each user provides their own.

Step 1: Create the Workflow

Start by creating a new workflow in the ServFlow dashboard.
  1. Open the ServFlow dashboard
  2. Click the + button in the Files Available panel
  3. Enter a name for your workflow: telegram-bot
  4. Click Create

Configure the HTTP Entry Point

Click on the HTTP Request Entry node that appears. In the settings panel on the right, configure:
FieldValue
Listen Path/telegram
HTTP MethodPOST
Telegram sends webhook payloads as POST requests with a JSON body containing message details. The key fields we’ll use are message.chat.id, message.from.id, and message.text.

Step 2: Check for User Credentials

The first action checks if the user has already stored their Binance credentials in the database.

Add a Fetch Action

  1. Click the + button below the HTTP Entry node
  2. Select Actions
  3. Choose fetch from the list

Configure the Fetch Action

Click on the new fetch node and configure these fields:
FieldValue
Namefetch credentials
Integration IDmongo-main
Tablecredentials
Single Result✅ Enabled
Should Fail✅ Enabled
Fail If Empty✅ Enabled

Add a Filter

In the Filters section, click Add Filter and configure:
FieldValue
Fielduser_id
Operation==
ComparatorSelect User ID from Telegram variables (see below)

Using Telegram Field Presets

When you click into the Comparator field and open the content editor, you’ll see Popular Variables in the left panel. Expand the Telegram section to find pre-configured variables:
Content editor showing Telegram preset variables including User ID, Message Body, and Chat ID
ServFlow automatically detects that this is a Telegram webhook and provides convenient presets:
VariableTemplate SyntaxDescription
User ID{{ body "message.from.id" }}The unique identifier of the user who sent the message
Message Body{{ body "message.text" }}The text content of the message
Chat ID{{ body "message.chat.id" }}The chat/conversation identifier
Click User ID to insert it, then click Apply.
These presets save you from memorizing Telegram’s payload structure. Just click the variable you need and it’s inserted automatically!

Set the Connections

We’ll connect the Next and Fail paths after creating the other actions:
  • Next → Will connect to the typing indicator (for users with credentials)
  • Fail → Will connect to command parsing (for users without credentials)

Step 3: Parse the Save Command

When a user doesn’t have credentials stored, we need to check if they’re sending a /save_creds command.

Add a JavaScript Action

  1. Click + to add a new action
  2. Select Actionsjavascript

Configure the JavaScript Action

FieldValue
Nameparse_command
In the Script field, enter:
function servflowRun(vars) {
  const paramText = "{{ body \"message.text\"}}";
  try {
    const text = paramText;
    const match = text.trim().match(/^\/(\w+)\s*(.*)$/);

    if (!match || match[1].toLowerCase() !== "save_creds") {
      return { command: "", args: [] };
    }

    const command = "/" + match[1];
    let args;

    try {
      args = JSON.parse(match[2]);
    } catch {
      args = match[2]?.trim() 
        ? match[2].trim().split(/\s+/) 
        : [];
    }

    return { command, args };
  } catch (err) {
    return { command: "", args: [] };
  }
}
This script parses messages like /save_creds api_key_here secret_key_here and extracts the command and arguments.

Step 4: Add the Save Credentials Conditional

Now we need a conditional to check if the parsed command is /save_creds.

Add a Conditional

  1. Click + below the parse_command action
  2. Select Branch
  3. Choose Conditional

Configure the Conditional

FieldValue
Nameis_save_creds
In the Expression field, enter:
{{ eq .parse_command.command "/save_creds" }}
This checks if the command extracted by the JavaScript action equals /save_creds.

Step 5: Save User Credentials

When the conditional is true (user sent a save command), we store their credentials.

Add a Store Action

  1. From the True path of the conditional, click +
  2. Select Actionsstore

Configure the Store Action

FieldValue
Namesave_creds
Integration IDmongo-main
Tablecredentials

Add Fields to Store

In the Fields section, add these fields:
Field NameValue
user_idSelect User ID from Telegram presets
api_key{{ index .parse_command.args 0 }}
api_secret{{ index .parse_command.args 1 }}
saved_at{{ now }}
Remember, you can use the Telegram presets from the content editor instead of typing {{ body "message.from.id" }} manually!

Step 6: Send Confirmation Message

After saving credentials, confirm to the user via Telegram.

Add an HTTP Action

  1. Click + below the save_creds action
  2. Select Actionshttp

Configure the HTTP Action

FieldValue
NameCredentials saved
HTTP MethodPOST
URLhttps://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendMessage
Fail If Response Empty✅ Enabled

Add Headers

Click Add Header:
HeaderValue
Content-Typeapplication/json

Configure Request Body

In the Request Body section, add:
FieldValue
chat_idSelect Chat ID from Telegram presets
textCredentials saved successfully

Step 7: Tell Users How to Save Credentials

When the conditional is false (user doesn’t have credentials and isn’t sending a save command), tell them how to save their credentials.

Add an HTTP Action

  1. From the False path of the conditional, click +
  2. Select Actionshttp

Configure the HTTP Action

FieldValue
NameAdd credentials
HTTP MethodPOST
URLhttps://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendMessage
Expected Response Code200
Fail If Response Empty✅ Enabled

Add Headers

HeaderValue
Content-Typeapplication/json

Configure Request Body

FieldValue
chat_idSelect Chat ID from Telegram presets
textPlease add credentials first with /save_creds <api_key> <secret_key>

Step 8: Send Typing Indicator

When a user has credentials stored, we first send a “typing” indicator so they know the bot is processing their request.

Add an HTTP Action

  1. Connect from the Next path of fetch credentials
  2. Add an http action

Configure the HTTP Action

FieldValue
NameIs typing
HTTP MethodGET
URLSee below
Fail If Response Empty✅ Enabled
For the URL field, enter:
https://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendChatAction?chat_id={{ body "message.chat.id" }}&action=typing
In the URL field, you can also use the content editor to insert Chat ID from the Telegram presets instead of typing the template syntax manually.

Step 9: Configure the AI Agent

The AI agent is the brain of the bot. It understands natural language requests and decides which tools to use.

Add an Agent Action

  1. Click + below the Is typing action
  2. Select Actionsagent

Configure the Agent Action

FieldValue
Nameagent_llm
Integration IDopen-ai-main
Conversation IDSelect Chat ID from Telegram presets
Return Last Message❌ Disabled

Set the System Prompt

In the System Prompt field, enter:
You are a helpful telegram bot for performing actions on behalf of a user. You have access to tools to help you achieve your request.

Set the User Prompt

For the User Prompt field, select Message Body from the Telegram presets (or enter {{ body "message.text" }}).
The Conversation ID uses the chat ID to maintain conversation context across messages. This enables natural multi-turn conversations where the AI remembers previous messages.

Step 10: Add Agent Tools

The AI agent needs tools to perform Binance operations. We’ll add two workflow tools.

Add the Price Difference Tool

In the Tool Configurations section of the agent, click Add Tool and configure:
FieldValue
Typeworkflow
Nameprice_difference
DescriptionThis is for getting the price history ticks for a certain period over every hour. The period is the number of 1hr intervals to get e.g 12, the symbol is the pair e.g BTCUSDT
Return Typestring
Return Value{{ escape (jsonout .get_price_difference) }}
Add parameters:
  • period
  • symbol

Add the Account Balance Tool

Click Add Tool again and configure:
FieldValue
Typeworkflow
Nameaccount_balance
DescriptionThis tool is for fetching the account balance of the user
Return Typestring
Return Value{{ .get_account_balance }}
Leave Parameters empty for this tool.

Step 11: Create Binance Tool Actions

Now create the actions that the agent’s tools will call.

Add Price Difference Action

  1. Add a new action (this will be called by the agent tool)
  2. Select Actionsbinance/pricedifference
FieldValue
NameBinance Price Difference
Integration IDtelegram_bot_cred_storage_binance
Interval1h
Period{{ tool_param "period" }}
Symbol{{ tool_param "symbol" }}

Add Account Balance Action

  1. Add another action
  2. Select Actionsbinance/accountbalance
FieldValue
Namefetch account balance
Integration IDtelegram_bot_cred_storage_binance
Futures❌ Disabled
Symbol(leave empty for all balances)

Connect Tools to Actions

Go back to the agent’s tool configurations and set the Start action for each tool:
  • price_difference tool → Start: Binance Price Difference
  • account_balance tool → Start: fetch account balance
The tool_param function accesses parameters passed by the AI agent when it decides to use a tool.

Step 12: Send the Response

After the AI agent processes the request, send the response back to Telegram.

Add an HTTP Action

  1. Click + from the Next path of the agent
  2. Select Actionshttp

Configure the HTTP Action

FieldValue
Nametelegram_reply
HTTP MethodPOST
URLhttps://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendMessage
Fail If Response Empty✅ Enabled

Add Headers

HeaderValue
Content-Typeapplication/json

Configure Request Body

FieldValue
chat_idSelect Chat ID from Telegram presets
text{{ .agent_llm | escape }}
The escape function ensures special characters in the AI response don’t break the JSON payload.

Step 13: Add Response Nodes

Add response nodes to properly terminate each workflow path.

Add Success Response

  1. Click + at the end of successful paths
  2. Select Responses
  3. Configure:
FieldValue
Namesuccess
Status Code200
Add a response field:
FieldValue
messageReply sent via Telegram

Add Error Response

Create another response for error paths:
FieldValue
Nameerror
Status Code500
Add a response field:
FieldValue
error{{ .error }}

Step 14: Configure the Dynamic Binance Integration

Here’s the key to making this work for multiple users: lazy-loaded integrations that use each user’s stored credentials.

Open the Config Tab

  1. Click on the Config tab (next to Workflow at the top)
  2. Find the Integrations section

Add a New Integration

Click Add Integration and configure:
FieldValue
IDtelegram_bot_cred_storage_binance
Typebinance
Lazy Load✅ Enabled

Configure Integration Settings

FieldValue
API Key{{ .fetch_credentials.api_key }}
Secret Key{{ .fetch_credentials.api_secret }}
Testnet❌ Disabled (or enable for testing)
Timeout30
Lazy Load means the integration is only initialized when first used. The credentials reference the data fetched from MongoDB in Step 2, so each user’s Binance actions use their own stored credentials!

Step 15: Connect All the Nodes

Now connect all the workflow paths:

From fetch credentials:

  • NextIs typing
  • Failparse_command

From parse_command:

  • Nextis_save_creds (conditional)

From is_save_creds conditional:

  • Truesave_creds
  • FalseAdd credentials

From save_creds:

  • NextCredentials saved
  • Failerror response

From Credentials saved:

  • Nextsuccess response

From Add credentials:

  • Nextsuccess response

From Is typing:

  • Nextagent_llm

From agent_llm:

  • Nexttelegram_reply
  • Failerror response

From telegram_reply:

  • Nextsuccess response
  • Failerror response

Step 16: Save Your Workflow

Click the Save button in the top-left corner to save your workflow.
ServFlow workflow showing all configured actions

Testing Your Bot

Using Test Mode

ServFlow’s test mode lets you simulate requests without setting up the Telegram webhook.
  1. Click the Play button in the top-right corner of the workflow editor
Test run button location in the ServFlow dashboard
  1. Enter a sample Telegram webhook payload:
{
  "message": {
    "message_id": 123,
    "from": {
      "id": 12345678,
      "first_name": "Test",
      "username": "testuser"
    },
    "chat": {
      "id": 12345678,
      "type": "private"
    },
    "text": "What is my account balance?"
  }
}
  1. Click Run and observe the workflow execution in the debug log

Testing Credential Save

Test the credential save flow with:
{
  "message": {
    "message_id": 124,
    "from": {
      "id": 12345678,
      "first_name": "Test",
      "username": "testuser"
    },
    "chat": {
      "id": 12345678,
      "type": "private"
    },
    "text": "/save_creds your_api_key your_secret_key"
  }
}

Going Live

Once testing is complete, connect your bot to Telegram.

1. Add Your Bot Token Secret

In the ServFlow dashboard, go to SettingsSecrets and add:
NameValue
TELEGRAM_BOT_TOKENYour bot token from BotFather

2. Set the Webhook URL

Tell Telegram where to send messages by calling the setWebhook API:
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-servflow-domain.com/telegram"}'
Replace:
  • <YOUR_BOT_TOKEN> with your actual bot token
  • your-servflow-domain.com with your ServFlow deployment URL
For local development, use a tunneling service like ngrok to expose your local ServFlow instance to the internet.

3. Verify the Webhook

Check that the webhook is configured correctly:
curl "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getWebhookInfo"
You should see your URL in the response with no errors.

4. Test with Telegram

Open your bot in Telegram and send a message! Try:
  • /save_creds your_api_key your_secret_key — Save your Binance credentials
  • “What’s my account balance?” — Check your Binance balance
  • “Show me BTC price history for the last 12 hours” — Get price data

Security Considerations

Important security notes:
  1. Never share your bot token — Anyone with the token can control your bot
  2. Use HTTPS — Always deploy with SSL to protect credentials in transit
  3. Consider encryption — For production, encrypt stored API credentials at rest
  4. Rate limiting — Consider adding rate limiting to prevent abuse
  5. API key permissions — Use Binance API keys with minimal required permissions

Extending the Bot

Now that you have a working bot, consider adding:
  • More Binance tools — Add spot/futures trading, order management
  • Price alerts — Schedule checks and notify users of price movements
  • Trade logging — Store trades to MongoDB for user history
  • Multi-exchange support — Add integrations for other exchanges

Complete Configuration Reference

For reference, here’s the complete YAML configuration:
enabled: true
id: telegram_bot_cred_storage

http:
  listenPath: /telegram
  method: POST
  next: action.fetch_credentials
  corsAllowedOrigins: []

actions:
  fetch_credentials:
    name: fetch_credentials
    type: fetch
    config:
      integrationID: mongo-main
      table: credentials
      filters:
        - field: user_id
          operation: "=="
          comparator: '{{ body "message.from.id" }}'
      single: true
      shouldFail: true
      failIfEmpty: true
    next: action.send_typing
    fail: action.parse_command

  parse_command:
    name: parse_command
    type: javascript
    config:
      script: |
        function servflowRun(vars) {
          const paramText = "{{ body \"message.text\"}}";
          try {
            const text = paramText;
            const match = text.trim().match(/^\/(\w+)\s*(.*)$/);
            if (!match || match[1].toLowerCase() !== "save_creds") {
              return { command: "", args: [] };
            }
            const command = "/" + match[1];
            let args;
            try {
              args = JSON.parse(match[2]);
            } catch {
              args = match[2]?.trim() ? match[2].trim().split(/\s+/) : [];
            }
            return { command, args };
          } catch (err) {
            return { command: "", args: [] };
          }
        }
    next: conditional.is_save_creds
    fail: response.error

  save_creds:
    name: save_creds
    type: store
    config:
      integrationID: mongo-main
      table: credentials
      fields:
        user_id: '{{ body "message.from.id" }}'
        api_key: '{{ index .parse_command.args 0 }}'
        api_secret: '{{ index .parse_command.args 1 }}'
        saved_at: '{{ now }}'
    next: action.confirm_save
    fail: response.error

  confirm_save:
    name: confirm_save
    type: http
    config:
      method: POST
      url: https://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendMessage
      headers:
        Content-Type: application/json
      body:
        chat_id: '{{ body "message.chat.id" }}'
        text: "Credentials saved successfully"
      failIfResponseEmpty: true
    next: response.success
    fail: response.error

  tell_user_to_save:
    name: tell_user_to_save
    type: http
    config:
      method: POST
      url: https://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendMessage
      headers:
        Content-Type: application/json
      body:
        chat_id: '{{ body "message.chat.id" }}'
        text: "Please add credentials first with /save_creds <api_key> <secret_key>"
      expectedResponseCode: "200"
      failIfResponseEmpty: true
    next: response.success
    fail: response.error

  send_typing:
    name: send_typing
    type: http
    config:
      method: GET
      url: https://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendChatAction?chat_id={{ body "message.chat.id" }}&action=typing
      failIfResponseEmpty: true
    next: action.agent_llm
    fail: response.error

  agent_llm:
    name: agent_llm
    type: agent
    config:
      integrationID: open-ai-main
      conversationID: '{{ body "message.chat.id" }}'
      systemPrompt: >
        You are a helpful telegram bot for performing actions on behalf of a user.
        You have access to tools to help you achieve your request.
      userPrompt: '{{ body "message.text" }}'
      returnLastMessage: false
      toolConfigs:
        - type: workflow
          workflowConfig:
            name: price_difference
            description: >
              This is for getting the price history ticks for a certain period
              over every hour. The period is the number of 1hr intervals to get
              e.g 12, the symbol is the pair e.g BTCUSDT
            params:
              - period
              - symbol
            start: action.get_price_difference
            returnValue: '{{ escape (jsonout .get_price_difference) }}'
            type: string
        - type: workflow
          workflowConfig:
            name: account_balance
            description: This tool is for fetching the account balance of the user
            params: []
            start: action.get_account_balance
            returnValue: '{{ .get_account_balance }}'
            type: string
    next: action.telegram_reply
    fail: response.error

  get_price_difference:
    name: get_price_difference
    type: binance/pricedifference
    config:
      integrationID: telegram_bot_cred_storage_binance
      interval: 1h
      period: '{{ tool_param "period" }}'
      symbol: '{{ tool_param "symbol" }}'

  get_account_balance:
    name: get_account_balance
    type: binance/accountbalance
    config:
      integrationID: telegram_bot_cred_storage_binance
      futures: false
      symbol: ""

  telegram_reply:
    name: telegram_reply
    type: http
    config:
      method: POST
      url: https://api.telegram.org/bot{{ secret "TELEGRAM_BOT_TOKEN" }}/sendMessage
      headers:
        Content-Type: application/json
      body:
        chat_id: '{{ body "message.chat.id" }}'
        text: '{{ .agent_llm | escape }}'
      failIfResponseEmpty: true
    next: response.success
    fail: response.error

conditionals:
  is_save_creds:
    name: is_save_creds
    expression: '{{ eq .parse_command.command "/save_creds" }}'
    onTrue: action.save_creds
    onFalse: action.tell_user_to_save

responses:
  success:
    name: success
    code: 200
    responseObject:
      fields:
        message:
          value: "Reply sent via Telegram"

  error:
    name: error
    code: 500
    responseObject:
      fields:
        error:
          value: '{{ .error }}'

integrations:
  telegram_bot_cred_storage_binance:
    type: binance
    lazyLoad: true
    config:
      api_key: '{{ .fetch_credentials.api_key }}'
      secret_key: '{{ .fetch_credentials.api_secret }}'
      testnet: false
      timeout: 30

Next Steps

AI Agents

Learn more about configuring AI agents and tools.

Binance Trading

Explore all available Binance actions.

Data Operations

Master MongoDB and database operations.

Secrets Management

Securely manage API tokens and credentials.