[Yandex Cloud documentation](../../index.md) > [Tutorials](../index.md) > [Serverless technologies](index.md) > AI > Developing a Telegram bot with AI agent support

# How to create a Telegram bot with AI agent support using Yandex Workflows


With serverless technologies, you can create a Telegram bot with [text generation model](https://aistudio.yandex.ru/docs/en/ai-studio/concepts/generation/models) support based on [Yandex Cloud AI Studio](https://aistudio.yandex.ru/docs/en/ai-studio/concepts/index).

In this tutorial, you will create a bot which provides movie recommendations based on user preferences. You will do this by creating an AI agent, arranging for data storage in [Yandex Object Storage](../../storage/index.md) and [Yandex Lockbox](../../lockbox/index.md), setting up bot logic in [Yandex Workflows](../../serverless-integrations/index.md), and a webhook to start using a link.

To create a bot:

1. [Get your cloud ready](#before-you-begin).
1. [Create a Telegram bot](#create-bot).
1. [Create a secret](#create-secret).
1. [Create a bucket](#create-bucket).
1. [Create a service account](#create-sa).
1. [Create an AI agent](#create-ai-agent).
1. [Set up a workflow](#config-workflow).
1. [Set up a webhook for your bot](#config-webhook).
1. [Test your bot](#check-result).
1. [Customize the agent](#what-is-next).

If you no longer need the resources you created, [delete them](#clear-out).


## Getting started {#before-you-begin}

Sign up for Yandex Cloud and create a [billing account](../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../billing/quickstart/index.md) and [link](../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../resource-manager/concepts/resources-hierarchy.md).


## Required paid resources {#paid-resources}

The cost of Telegram bot support includes:

* Fee for text generation (see [Yandex Cloud AI Studio pricing](https://aistudio.yandex.ru/docs/en/ai-studio/pricing)).
* Fee for storing the secret and requests to the secret (see [Yandex Lockbox pricing](../../lockbox/pricing.md)).
* Fee for storage space occupied by data, number of data operations, and outgoing traffic (see [Yandex Object Storage pricing](../../storage/pricing.md)).
* Fee for retrieval and storage of logs (see [Yandex Cloud Logging pricing](../../logging/pricing.md)).


## Create a Telegram bot {#create-bot}

Create a bot in Telegram and get a token.

1. To register a new bot, start [BotFather](https://t.me/BotFather) and run this command:

    ```text
    /newbot
    ```

1. Name your bot, e.g., `Serverless AI Telegram Bot`. This is the name users will see when chatting with the bot.
1. Specify a username for your bot, e.g., `ServerlessAITelegramBot`. You can use it to find the bot in Telegram. The username must end with `...Bot` or `..._bot`.

    As a result, you will get a token. Save it, as you will need it later.


## Create a secret {#create-secret}

Create a [secret](../../lockbox/concepts/secret.md) to store the Telegram API access token.

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) where you are going to create your infrastructure.
  1. Navigate to **Lockbox**.
  1. Click **Create secret**.
  1. In the **Name** field, enter a name for the secret.
  1. Select the `Custom` secret type.
  1. In the **Key** field, enter `token`.
  1. In the **Value** field, specify the token you got when [creating](#create-bot) the bot.
  1. Click **Create**.

- Yandex Cloud CLI {#cli}

  If you do not have the Yandex Cloud CLI yet, [install and initialize it](../../cli/quickstart.md#install).

  The folder used by default is the one specified when [creating](../../cli/operations/profile/profile-create.md) the CLI profile. To change the default folder, use the `yc config set folder-id <folder_ID>` command. You can also specify a different folder for any command using `--folder-name` or `--folder-id`. If you access a resource by its name, the search will be limited to the default folder. If you access a resource by its ID, the search will be global, i.e., through all folders based on access permissions.

  1. See the description of the CLI command for creating a secret:

      ```bash
      yc lockbox secret create --help
      ```

  1. Create a secret:

      ```bash
      yc lockbox secret create \
        --name tg-bot-token \
        --payload '[{"key":"token","text_value":"<bot_token>"}]'
      ```

      Where:

      * `--name`: Secret name.
      * `--payload`: Secret contents as a YAML or JSON array:

          * `key`: Secret key.
          * `text_value`: Secret value. Specify the token you got when [creating the bot](#create-bot).

      Result:

      ```text
      id: e6qf05v4ftms********
      folder_id: b1g681qpemb4********
      created_at: "2025-08-20T12:26:02.961Z"
      name: tg-bot-token
      status: ACTIVE
      current_version:
        id: e6q768pl3vrf********
        secret_id: e6qf05v4ftms********
        created_at: "2025-08-20T12:26:02.961Z"
        status: ACTIVE
        payload_entry_keys:
          - token
      ```

- API {#api}

  To create a secret, use the [Create](../../lockbox/api-ref/Secret/create.md) REST API method for the [Secret](../../lockbox/api-ref/Secret/index.md) resource or the [SecretService/Create](../../lockbox/api-ref/grpc/Secret/create.md) gRPC API call.

{% endlist %}


## Create a bucket {#create-bucket}

Create a [bucket](../../storage/concepts/bucket.md) to store your chat history with the bot.

{% list tabs group=instructions %}

- Management console {#console}

  1. Open the [management console](https://console.yandex.cloud).
  1. Navigate to **Object Storage**.
  1. In the top panel, click **Create bucket**.
  1. Specify a bucket name that meets [these naming conventions](../../storage/concepts/bucket.md#naming).
  1. Specify the maximum bucket size: `5 GB`.
  1. Click **Create bucket**.

- Yandex Cloud CLI {#cli}

  1. See the description of the CLI command for creating a bucket:

      ```bash
      yc storage bucket create --help
      ```

  1. Create a bucket in the default [folder](../../resource-manager/concepts/resources-hierarchy.md#folder):

      ```bash
      yc storage bucket create \
        --name <bucket_name> \
        --default-storage-class standard \
        --max-size 5368709120
      ```

      Where:

      * `--name`: Bucket name that meets the [naming conventions](../../storage/concepts/bucket.md#naming).
      * `--default-storage-class`: [Storage class](../../storage/concepts/storage-class.md).
      * `--max-size`: Maximum bucket size, in bytes.

      Result:

      ```text
      name: bot-history-storage
      folder_id: b1g681qpemb4********
      anonymous_access_flags: {}
      default_storage_class: STANDARD
      versioning: VERSIONING_DISABLED
      max_size: "5368709120"
      created_at: "2025-08-20T12:23:21.361186Z"
      resource_id: e3erbgk1qmih********
      ```

- AWS CLI {#aws-cli}

  If you do not have the AWS CLI yet, [install and configure it](../../storage/tools/aws-cli.md).

  To create a bucket, [assign](../../iam/operations/sa/assign-role-for-sa.md) the `storage.editor` [role](../../storage/security/index.md#storage-editor) to the service account used by the AWS CLI.

  In the terminal, run this command:

  ```bash
  aws s3api create-bucket \
    --endpoint-url=https://storage.yandexcloud.net \
    --bucket <bucket_name>
  ```

  Where:

  * `--endpoint-url`: Object Storage endpoint.
  * `--bucket`: Bucket name that meets the [naming conventions](../../storage/concepts/bucket.md#naming).

- API {#api}

  To create a bucket, use the [Create](../../storage/api-ref/Bucket/create.md) REST API method for the [Bucket](../../storage/api-ref/Bucket/index.md) resource, the [BucketService/Create](../../storage/api-ref/grpc/Bucket/create.md) gRPC API call, or the [create](../../storage/s3/api-ref/bucket/create.md) S3 API method.

{% endlist %}


## Create a service account {#create-sa}

Create a [service account](../../iam/concepts/users/service-accounts.md) named `sa-workflows`, which you will use to execute the workflow steps.

{% list tabs group=instructions %}

- Management console {#console}

  1. Open the [management console](https://console.yandex.cloud).
  1. Navigate to **Identity and Access Management**.
  1. Click **Create service account**.
  1. Enter the service account name: `sa-workflows`.
  1. Click ![image](../../_assets/console-icons/plus.svg) **Add role** and assign these [roles](../../iam/roles-reference.md):

      * `storage.uploader`
      * `storage.viewer`
      * `lockbox.payloadViewer`
      * `ai.languageModels.user`
      * `ai.assistants.editor`

  1. Click **Create**.

- Yandex Cloud CLI {#cli}

  1. If you do not have [jq](https://stedolan.github.io/jq/download/) yet, install it.

  1. See the description of the CLI command for creating a service account:

      ```bash
      yc iam service-account create --help
      ```

  1. Create a service account:

      ```bash
      yc iam service-account create --name sa-workflows
      ```

      Where `--name` is the service account name.

      Result:

      ```text
      id: ajersnus6rb2********
      folder_id: b1g681qpemb4********
      created_at: "2025-08-20T12:18:41.869376672Z"
      name: sa-workflows
      ```

  1. Save the service account ID and the folder ID to these variables:

      ```bash
      WF_SA=$(yc iam service-account get --name sa-workflows --format json | jq -r .id)
      FOLDER_ID=$(yc config get folder-id)
      ```

  1. See the description of the CLI command for assigning a [role](../../iam/roles-reference.md) for the folder:

      ```bash
      yc resource-manager folder add-access-binding --help
      ```

  1. Assign the following roles for the folder to the service account:

      ```bash
      yc resource-manager folder add-access-binding \
        --id $FOLDER_ID \
        --role storage.uploader \
        --subject serviceAccount:$WF_SA

      yc resource-manager folder add-access-binding \
        --id $FOLDER_ID \
        --role storage.viewer \
        --subject serviceAccount:$WF_SA

      yc resource-manager folder add-access-binding \
        --id $FOLDER_ID \
        --role lockbox.payloadViewer \
        --subject serviceAccount:$WF_SA

      yc resource-manager folder add-access-binding \
        --id $FOLDER_ID \
        --role ai.languageModels.user \
        --subject serviceAccount:$WF_SA

      yc resource-manager folder add-access-binding \
        --id $FOLDER_ID \
        --role ai.assistants.editor \
        --subject serviceAccount:$WF_SA
      ```

      Where:

      * `--id`: Folder ID.
      * `--role`: Role.
      * `--subject`: Service account ID.

      Result:

      ```text
      effective_deltas:
        - action: ADD
          access_binding:
            role_id: ai.languageModels.user
            subject:
              id: ajersnus6rb2********
              type: serviceAccount
      ```

- API {#api}

  Create a service account named `sa-workflows` with the following roles:

  * `storage.uploader`
  * `storage.viewer`
  * `lockbox.payloadViewer`
  * `ai.languageModels.user`
  * `ai.assistants.editor`

  To create a service account, use the [Create](../../iam/api-ref/ServiceAccount/create.md) REST API method for the [ServiceAccount](../../iam/api-ref/ServiceAccount/index.md) resource or the [ServiceAccountService/Create](../../iam/api-ref/grpc/ServiceAccount/create.md) gRPC API call.

  To assign a role to a service account, use the [updateAccessBindings](../../iam/api-ref/ServiceAccount/updateAccessBindings.md) REST API method for the [ServiceAccount](../../iam/api-ref/ServiceAccount/index.md) resource or the [ServiceAccountService/UpdateAccessBindings](../../iam/api-ref/grpc/ServiceAccount/updateAccessBindings.md) gRPC API call.

{% endlist %}


## Create an AI agent {#create-ai-agent}

Create a [text agent](https://aistudio.yandex.ru/docs/en/ai-studio/concepts/agents/text-agents) in AI Studio to process user requests.

{% list tabs group=instructions %}

- Management console {#console}

  1. Open the [AI Studio interface](https://aistudio.yandex.ru/platform).
  1. Click **Create AI agent** → **Create agent**.
  1. In the **Name** field, enter a name for the agent, e.g., `Cinephile agent`.
  1. In the **Instruction** field, enter an instruction for the agent:

      ```
      You are a movie selection consultant
      
      You goal is to help the user find a movie to watch based on their preferences.
      On first request, ask them to name some of their favorite movies (one per line).
      Use this information to make recommendations and ask clarifying questions.
      
      History of previous conversations: not_var{{ backstory }}
      ```

      {% note info %}

      The `not_var{{ backstory }}` variable is used to provide conversation history to the agent. It allows the agent to be aware of the user’s previous messages when generating a response.

      {% endnote %}

  1. Click **Create**.
  1. Copy the ID of the agent you created by clicking **ID** ![image](../../_assets/console-icons/copy.svg) at the top left. Save it. You will need this ID to set up a workflow.

{% endlist %}


## Set up a workflow {#config-workflow}

Set up a workflow to read and store chat history, call the AI agent, and send responses to Telegram.

{% note tip %}

This guide describes how to create a workflow using the YaWL specification; however, you can also create and edit workflows using the [constructor](../../serverless-integrations/operations/workflows/constructor/index.md).

{% endnote %}

![telegram-ai-bot-workflows-workflow](../../_assets/tutorials/telegram-ai-bot-workflows-workflow.png)


### Prepare a YaWL specification {#prepare-spec-wf}

Save the workflow [YaWL specification](../../serverless-integrations/concepts/workflows/yawl/index.md) to a YAML file, e.g., `yawl-spec.yaml`.

```yaml
yawl: '0.1'
start: do_work
steps:
  do_work:
    parallel:
      branches:

        # Step that sends a typing action to make the chat feel more responsive
        send_typing_action:
          start: send_typing_action
          steps:
            send_typing_action:
              httpCall:
                url: >-
                  https://api.telegram.org/bot\(lockboxPayload("<secret_ID>"; "token"))/sendChatAction
                method: POST
                headers:
                  Content-Type: application/json
                body: |
                  \({
                    chat_id: .input.message.chat.id,
                    action: "typing"
                  })

        # Main logic
        handle_update:
          start: get_history
          steps:
            get_history:
              objectStorage:
                bucket: <bucket_name>
                object: history/\(.input.message.chat.id).json
                get:
                  contentType: JSON
                output: '\({history: .Content})'
                next: call_ai
                catch:
                  - errorList:
                      - STEP_INVALID_ARGUMENT # There is no file or it is not JSON -> initialize.
                    errorListMode: INCLUDE
                    output: '\({history: []})'
                    next: call_ai

            call_ai:
              aiStudioAgent:
                promptTemplateId: <agent_ID>
                message: \(.input.message.text)
                variables:
                  backstory: >-
                    History of previous conversations (JSON array of objects)
                    {role,message}): "\(.history)"
                output: '\({reply: .Result})'
                next: send_reply

            send_reply:
              telegramBot:
                token: \(lockboxPayload("<secret_ID>"; "token"))
                sendMessage:
                  chatId: \(.input.message.chat.id)
                  text: \(.reply)
                  replyTo: \(.input.message.message_id)
                  parseMode: MARKDOWN
                next: save_history

            save_history:
              objectStorage:
                bucket: <bucket_name>
                object: history/\(.input.message.chat.id).json
                put:
                  contentType: JSON
                  content: >-
                    \(
                      .history +
                      [
                        {role:"user", message:.input.message.text},
                        {role:"assistant", message:.reply}
                      ]
                    )
```

Where:

* `<bucket_name>`: Name of the bucket you [created earlier](#create-bucket).
* `<secret_ID>`: ID of the secret you [created earlier](#create-secret).
* `<agent_ID>`: ID of the agent [you created earlier](#create-ai-agent).


### Create a workflow {#create-workflow}

{% list tabs group=instructions %}

- Management console {#console}

  1. Open the [management console](https://console.yandex.cloud).
  1. Navigate to **Serverless Integrations**.
  1. In the left-hand panel, click ![image](../../_assets/console-icons/graph-node.svg) **Workflows**.
  1. In the top-right corner, click **Create workflow**.
  1. Select the `YaML specification` method.
  1. In the code editor, paste the text of the previously prepared YaWL workflow specification.
  1. Expand **Additional parameters**:

      1. Enter a name for the workflow. The naming requirements are as follows:

          * Length: between 3 and 63 characters.
          * It can only contain lowercase Latin letters, numbers, and hyphens.
          * It must start with a letter and cannot end with a hyphen.

      1. Select the `sa-workflows` service account.
      1. Under **Logging**, disable **Write logs** if you do not want to pay for storing logs.

  1. Click **Create**.

- Yandex Cloud CLI {#cli}

  1. See the description of the CLI command for creating a workflow:

      ```bash
      yc serverless workflow create --help
      ```

  1. Create a workflow:

      ```bash
      yc serverless workflow create \
        --yaml-spec <specification_file> \
        --name <workflow_name> \
        --service-account-id $WF_SA
      ```

      Where:

      * `--yaml-spec`: Path to the file with the workflow YaWL specification prepared earlier, e.g., `./yawl-spec.yaml`.
      * `--name`: Workflow name. The naming requirements are as follows:

          * Length: between 3 and 63 characters.
          * It can only contain lowercase Latin letters, numbers, and hyphens.
          * It must start with a letter and cannot end with a hyphen.

      * `--service-account-id`: `sa-workflows` service account ID.

      Result:

      ```text
      id: dfqjl5hh5p90********
      folder_id: b1g681qpemb4********
      specification:
        spec_yaml: "yawl: ..."
      created_at: "2025-03-11T09:27:51.691990Z"
      name: my-workflow
      status: ACTIVE
      log_options: {}
      service_account_id: aje4tpd9coa********
      execution_url: https://serverless-workflows.api.cloud.yandex.net/workflows/v1/execution/dfq0eod50iol********/start
      ```

- API {#api}

  To create a workflow, use the [Create](../../serverless-integrations/workflows/api-ref/Workflow/create.md) REST API method for the [Workflows](../../serverless-integrations/workflows/api-ref/Workflow/index.md) resource or the [Workflow/Create](../../serverless-integrations/workflows/api-ref/grpc/Workflow/create.md) gRPC API call.

{% endlist %}


### Make the workflow public {#make-public}

Make the workflow public so it can be executed via a link without authentication.

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder containing the [workflow](../../serverless-integrations/concepts/workflows/workflow.md).
  1. Navigate to **Serverless Integrations**.
  1. In the left-hand panel, click ![image](../../_assets/console-icons/graph-node.svg) **Workflows**.
  1. Select the workflow.
  1. Enable **Public workflow**.
  1. Click **Save**.

- Yandex Cloud CLI {#cli}

  1. View the description of the CLI command for updating a [workflow](../../serverless-integrations/concepts/workflows/workflow.md):

      ```bash
      yc serverless workflow update --help
      ```

  1. Make the workflow public:

      ```bash
      yc serverless workflow update \
        --name <workflow_name> \
        --set-is-public
      ```

      Result:

      ```text
      id: dfqjl5hh5p90********
      ...
      is_public: true
      execution_url: https://serverless-workflows.api.cloud.yandex.net/workflows/v1/execution/dfq0eod50iol********/start
      ```

- API {#api}

  To make a [workflow](../../serverless-integrations/concepts/workflows/workflow.md) public, use the [Update](../../serverless-integrations/workflows/api-ref/Workflow/update.md) REST API method for the [Workflows](../../serverless-integrations/workflows/api-ref/Workflow/index.md) resource or the [workflow/Update](../../serverless-integrations/workflows/api-ref/grpc/Workflow/update.md) gRPC API call with `isPublic: true`.

{% endlist %}

{% note info %}

Any user can execute a public workflow without an IAM token. This is necessary for setting up a Telegram webhook to send workflow execution requests via a link.

{% endnote %}


## Set up a webhook for your bot {#config-webhook}

Set up a webhook for your bot for it to send workflow execution requests via a link.


### Get a workflow execution link {#get-execution-url}

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder containing the workflow.
  1. Navigate to **Serverless Integrations**.
  1. In the left-hand panel, click ![image](../../_assets/console-icons/graph-node.svg) **Workflows**.
  1. Select a workflow. The execution link will appear in the **Start URL** field.

- Yandex Cloud CLI {#cli}

  To get an execution link, run this command:

  ```bash
  yc serverless workflow get <workflow_name>
  ```

  Result:

  ```text
  id: dfqjl5hh5p90********
  ...
  is_public: true
  execution_url: https://serverless-workflows.api.cloud.yandex.net/workflows/v1/execution/dfq0eod50iol********/start
  ```

  Save the value of the `execution_url` field.

- API {#api}

  To get a workflow execution link, use the [get](../../serverless-integrations/workflows/api-ref/Workflow/get.md) REST API method for the [Workflow](../../serverless-integrations/workflows/api-ref/Workflow/index.md) resource or the [WorkflowsService/Get](../../serverless-integrations/workflows/api-ref/grpc/Workflow/get.md) gRPC API call. The execution link will appear in the `execution_url` field.

{% endlist %}


### Set up a webhook {#setup-webhook}

If you do not have [cURL](https://curl.haxx.se) yet, install it.

The example below is for MacOS and Linux. To run it on Windows, [check the details on working with Bash in Microsoft Windows](../../overview/concepts/console-syntax-guide.md).

Set up a webhook for your bot:

{% list tabs group=instructions %}

- Bash {#bash}

  Run this command:

  ```bash
  curl -s "https://api.telegram.org/bot<bot_token>/setWebhook" \
    -d "url=<execution_url>"
  ```

  Where:

  * `<bot_token>`: Token you got when [creating the bot](#create-bot).
  * `<execution_url>`: Workflow execution link you got in the [previous step](#get-execution-url).

  Here is an example:

  ```bash
  curl -s "https://api.telegram.org/bot1357246809:AAFhSteLniAw71g8jx6K5kTErO3********/setWebhook" \
    -d "url=https://serverless-workflows.api.cloud.yandex.net/workflows/v1/execution/fd2g4pu20roc********/start"
  ```

  Result:

  ```text
  {"ok":true,"result":true,"description":"Webhook was set"}
  ```

{% endlist %}


## Test your bot {#check-result}

1. Find the Telegram bot you created [earlier](#create-bot) by its username.
1. Click **START** to start a chat.
1. Send the bot a list of movie titles, one per line.

    Here is an example:

    ```text
    Movie 1
    Movie 2
    Movie 3
    ```

    Bot's response:

    ```text
    Hi there! Thank you for letting me know your preferences. Here are the movies I would recommend based on your tastes:
    ...
    Which of these movies would you like to watch? Or do you have some other favorite movies you want me to consider?
    ```


#### What's next {#what-is-next}

Try editing the agent's instruction in AI Studio to suit your task. For example, edit the agent's instruction to make it select music artists:

```
You are a music artist selection consultant

You goal is to help the user find music to listen to based on their preferences.
On first request, ask them to name some of their favorite bands, artists,
composers, and genres (one per line).
Use this information to make recommendations, ask clarifying questions.

History of previous conversations: not_var{{ backstory }}
```

Also, you can:
* Add text or files as sources of information for the agent. For more information, see [Text-based agents in AI Studio](https://aistudio.yandex.ru/docs/en/ai-studio/concepts/agents/text-agents).
* Configure conversation context management. To learn more, see [Conversation context management](https://aistudio.yandex.ru/docs/en/ai-studio/operations/agents/manage-context).
* Use the agent's other tools, such as file search or web search.


## How to delete the resources you created {#clear-out}

To stop [paying](#paid-resources) for the resources you no longer need, delete them:

1. [Delete](../../serverless-integrations/operations/workflows/workflow/delete.md) the workflow.
1. [Delete](../../storage/operations/buckets/delete.md) the bucket.
1. [Delete](../../lockbox/operations/secret-delete.md) the secret.
1. Delete the AI agent in AI Studio.
1. If the workflow logging feature was left on, [delete](../../logging/operations/delete-group.md) the log group.