Standalone Bot YAML

Describe a standalone gateway + agent + channels declaratively

Describe your agent, gateway, and channel platforms in a single YAML file — no Python required.

python
from praisonaiagents import Agent

agent = Agent(name="bot-agent", instructions="Reply to users across chat channels.")
agent.start("Answer incoming messages from Telegram and Discord.")

The user defines a bot.yaml; the gateway loads it and connects the agent to every configured channel.

Diagram

Standalone Bot YAML

📄 bot.yaml

🗼 Gateway

🤖 Agent

✅ Channel Reply

How It Works

Diagram
AgentGatewayChannelUserAgentGatewayChannelUserMessage on Telegram / DiscordForward inbound eventRun configured agentReply on the same channel

Config Structure

Diagram

YAML Config

📄 bot.yaml

agent

gateway

platforms

🤖 Agent

🗼 WebSocket\nGateway

💬 Telegram

🎮 Discord

Quick Start

  1. Install
    bash
    pip install praisonaiagents "praisonai-bot[gateway,bot]"
  2. Set credentials
    bash
    export OPENAI_API_KEY=your_openai_api_key
    export TELEGRAM_BOT_TOKEN=your_telegram_token
  3. Create bot.yaml

    Leave the model unset to resolve it from whichever provider credential is present — no OpenAI key required:

    yaml
    # bot.yaml — auto-detect provider from whichever API key is set
    agent:
      name: assistant
      instructions: You are a helpful assistant.
      # model omitted ⇒ resolves to the detected provider at load time.
      # Equivalent to:  model: auto
    
    platform: telegram
    token: ${TELEGRAM_BOT_TOKEN}

    Or pin a specific model explicitly — an explicit value always wins:

    yaml
    agent:
      name: assistant
      instructions: You are a helpful assistant.
      model: gpt-4o-mini
    
    gateway:
      host: 127.0.0.1
      port: 8765
    
    platforms:
      telegram:
        token: ${TELEGRAM_BOT_TOKEN}
  4. Start the gateway with your YAML
    bash
    praisonai-bot gateway start --config bot.yaml

    The gateway registers the agent and starts all configured channel bots automatically.


Config Options Reference

agent section

KeyTypeDefaultDescription
agent.namestrrequiredAgent display name, used as the agent_id for gateway routing
agent.instructionsstrrequiredSystem prompt / instructions for the agent
agent.modelstrNone → provider-detected (last resort "gpt-4o-mini")LLM model identifier. Leave unset or set to "auto" to resolve to the detected provider’s default (Anthropic / Gemini / Ollama / LM Studio / OpenAI). An explicit value always wins.
agent.llmstralias of modelBackward-compatible alias for agent.model. llm wins if both are set — the schema’s handle_model_aliases copies it onto model.

Provider auto-detection (model: auto)

Omitting model: and writing model: auto are equivalent — both defer the choice to load time, where the gateway picks a model that matches whichever provider credential is actually present. This is the same detection ladder praisonai setup uses (default_model_for_available_provider), so a bot config is no longer tied to an OpenAI key.

Detection walks the available credentials in this order and returns the first match:

OrderCredentialResolved model (example)
1OPENAI_API_KEYgpt-4o-mini
2ANTHROPIC_API_KEYanthropic/claude-3-5-sonnet-latest
3GEMINI_API_KEY / GOOGLE_API_KEYgemini/gemini-1.5-flash
4Local endpoint (Ollama / LM Studio)the local default
…other catalogued providers (Groq, Cohere, OpenRouter, …)that provider’s default
lastnone presentgpt-4o-mini (fallback)

A detected non-OpenAI provider yields a provider-prefixed id (e.g. anthropic/claude-3-5-sonnet-latest). The list above is illustrative — the SDK helper default_model_for_available_provider is the ground truth, so new providers are picked up automatically.

Precedence (highest wins):

Diagram

🔧 explicit agent.model
or –model

✅ Resolved model

⚙️ gateway default_model

🔮 model: auto / unset
→ detect provider

🪫 gpt-4o-mini
last-resort fallback

An explicit model: on the agent always wins; a default_model on the gateway block wins over detection; model: auto (or an unset model) triggers detection; gpt-4o-mini is used only when nothing else resolves.

gateway section

KeyTypeDefaultDescription
gateway.hoststr"127.0.0.1"Host to bind the WebSocket server
gateway.portint8765Port to listen on

platforms section

Each key under platforms is a platform name. Use ${VAR_NAME} for environment variable interpolation.

Key patternTypeDescription
platforms.<name>.tokenstrBot token for the platform. Supports ${ENV_VAR} substitution
platforms.slack.app_tokenstrSlack App-Level Token (xapp-...) for Socket Mode. Required for Slack when Socket Mode is used. Supports ${ENV_VAR} and secret references.

Supported platforms: telegram, discord, slack, whatsapp, and any platform registered via entry points.

events — inbound platform events (opt-in)

Add an events: list under a channel to receive reactions, edits, deletions, member changes, and thread creations:

yaml
channels:
  my-discord-bot:
    platform: discord
    token: "${DISCORD_BOT_TOKEN}"
    events: [reactions, edits, members, threads]

See Inbound Platform Events for the full list of accepted classes and what each one enables.

Single-platform shorthand

For one platform, declare a top-level platform: (and optionally token:) instead of a platforms: map:

yaml
platform: telegram
token: "${TELEGRAM_BOT_TOKEN}"

token: is optional — omit it and the declared platform: resolves the platform’s own documented env var (TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, etc.):

yaml
platform: telegram

The declared platform always wins. If other platform credentials are exported in the environment, bot start refuses to auto-enable them and logs a warning:

WARNING Config declares 'platform: telegram'; NOT auto-enabling discord, slack
despite their credentials being present in the environment. The declared
platform wins. To run them too, list them under 'channels:' explicitly.

Admission policy (unknown_user_policy / owner_user_id)

Control who may DM a single-bot instance directly from bot.yaml. Since PR #5111, praisonai bot start wires unknown_user_policy and owner_user_id into the running bot for telegram / discord / slack — matching the gateway path exactly.

yaml
# bot.yaml — single-bot admission policy
platform: telegram
token: ${TELEGRAM_BOT_TOKEN}
unknown_user_policy: allow        # deny (default) | pair | allow
owner_user_id: 987654321          # required when unknown_user_policy: pair
agent:
  name: Assistant
  instructions: Reply to incoming messages.

Both fields also work at the channel level for the multi-channel shape:

yaml
# bot.yaml — channel-level admission policy
channels:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}
    unknown_user_policy: pair
    owner_user_id: 987654321
agent:
  name: Assistant
  instructions: Reply to incoming messages.
ValueMeaning
denyDefault. Drop inbound DMs from users not on allowed_users. Since PR #5112 every drop logs a WARNING with the sender’s user_id, platform, and conversation so operators can distinguish a misconfigured allowlist from a broken bot.
pairRoute unknown users through the owner-approval / pairing flow. Requires owner_user_id.
allowOpen DMs to everyone. Trusted networks / open bots only.

Values are case-insensitive on load (ALLOW normalises to allow) and whitespace is trimmed. An unquoted numeric owner_user_id (e.g. 987654321) is coerced to a string automatically.

Unknown-User Pairing

Full owner-approval flow, inline buttons, and CLI fallback.

Bot-to-bot inbound (allow_bots / bot_loop_protection)

Wire the bot-to-bot opt-in and the runaway-loop guard directly from bot.yaml — since PR #5242, praisonai bot start reads both allow_bots and bot_loop_protection for telegram / discord / slack and folds them into BotConfig, exactly matching the gateway path.

yaml
# bot.yaml
name: Group Helper
platform: telegram
allow_bots: true
bot_loop_protection:
  max_events_per_window: 20
  window_seconds: 60
  cooldown_seconds: 60

Default-off preserves today’s “drop bot-authored inbound messages” behaviour. See Bot Loop Protection for the full policy reference.

Slack single-bot shorthand

Slack Socket Mode also needs an app-level token, so add a top-level app_token: alongside token::

yaml
platform: slack
token: "${SLACK_BOT_TOKEN}"
app_token: "${SLACK_APP_TOKEN}"

app_token: accepts the same three forms as token: — plaintext, ${ENV} interpolation, and the { source, id } secret reference.

Run a Slack bot in three lines plus the agent block:

yaml
# bot.yaml — Slack Socket Mode single-bot shorthand
platform: slack
token: "${SLACK_BOT_TOKEN}"
app_token: "${SLACK_APP_TOKEN}"

agent:
  name: assistant
  instructions: Reply to Slack mentions.
  llm: gpt-4o-mini
bash
export SLACK_BOT_TOKEN=xoxb-...
export SLACK_APP_TOKEN=xapp-...
praisonai bot start --config bot.yaml
Diagram

slack

telegram / discord

app_token

dropped for non-Slack

📄 bot.yaml

platform:

✅ token + app_token

✅ token only

⚠️ ignored

🔌 Slack Socket Mode

Shell execution keys

Add these per-channel keys to let a bot run shell commands with approval routing.

Key patternTypeDefaultDescription
<name>.allow_shellboolFalseOpt in shell execution. Injects execute_command, lifts the deny-list entry, and appends a permission-grant line to instructions.
<name>.auto_approve_shellboolTrueRuns every shell call immediately only on a loopback-bound gateway + non-group channel. On externally-bound gateways or any channel with a group_policy, the gateway automatically downgrades this to an approval backend (see <name>.auto_approve_shell_acknowledge_exposed).
<name>.auto_approve_shell_acknowledge_exposedboolFalseExplicit acknowledgement that blanket auto-approval is safe on this externally-exposed or multi-user channel. Set to true (with auto_approve_shell: true) to keep the blanket behaviour on 0.0.0.0 binds or group channels. Off by default — the gateway fails closed to approval.
<name>.approval_channelstrNoneWhere the approval prompt is delivered (platform-specific ID).
<name>.approval_usersstr \list[str]NoneApprover allowlist. Comma-separated string or list.
<name>.approval_modestrNone (= channel)Backend selector: channel, gateway, http, webhook. Typos raise ValueError.
<name>.approval_webhook_urlstrNoneHTTPS URL for approval_mode: webhook.
<name>.approval_http_hoststr"127.0.0.1"HTTP dashboard host for approval_mode: http.
<name>.approval_http_portint8899HTTP dashboard port for approval_mode: http.
Bot Shell Execution

Full routing ladder, per-platform ID resolution, and recipes.


Environment Variable Interpolation

Values in the form ${VAR_NAME} are resolved from the current environment at startup:

yaml
platforms:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}   # reads os.environ["TELEGRAM_BOT_TOKEN"]
  discord:
    token: ${DISCORD_BOT_TOKEN}    # reads os.environ["DISCORD_BOT_TOKEN"]

Tokens stored in ~/.praisonai/.env (written by praisonai-bot onboard) are loaded automatically before interpolation.


When to Pick YAML vs Python

Diagram

Non-developer / ops

Developer / code-first

Who configures it?

📄 YAML config

🐍 Python script

✅ No code to maintain

✅ Easy credential rotation

✅ Works with praisonai-bot gateway start

✅ Programmatic agent construction

✅ Dynamic agent_id or custom registration

✅ Custom middleware / hooks

ScenarioUse
Operations team manages tokensYAML — credentials stay out of code
Static single-agent setupYAML — fewer moving parts
Dynamic agents (created at runtime)Python — full control over register_agent()
Custom gateway middleware or hooksPython — subclass or extend WebSocketGateway
Multiple environments (dev / staging / prod)YAML — per-environment files with env vars

Common Patterns

Multi-platform setup

yaml
agent:
  name: assistant
  instructions: You are a helpful assistant.
  llm: gpt-4o-mini

gateway:
  host: 0.0.0.0
  port: 8765

platforms:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}
  discord:
    token: ${DISCORD_BOT_TOKEN}
  slack:
    token: ${SLACK_BOT_TOKEN}

Custom port from environment

yaml
gateway:
  host: 0.0.0.0
  port: 9000

Or set the GATEWAY_PORT environment variable — the CLI reads it automatically when --port is not passed.


Best Practices

Always use ${…} for tokens

Never hardcode tokens in YAML. Use environment variables and store them in ~/.praisonai/.env (written by praisonai-bot onboard) or your secrets manager.

yaml
# Good
platforms:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}

# Never do this
platforms:
  telegram:
    token: 1234567890:ABCdef...
Run praisonai-bot gateway doctor before going live
bash
praisonai-bot gateway doctor --config bot.yaml

This validates every channel token before starting, so a bad token fails fast instead of silently reconnecting. It also prints a credential availability table for token, app_token, and verify_token — including the secret-reference form — without revealing any value.

Use separate YAML files per environment

Keep dev.yaml, staging.yaml, and prod.yaml — each referencing different environment variables. Pass the right one with --config.


Related