> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Proactive Delivery

> Send messages from your bot to any channel — by origin, platform, channel ID, or friendly alias.

Push outbound messages from an agent-backed bot — reply to the requesting chat, a home channel, or a named alias.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import asyncio
from praisonai.bots import BotOS, Bot, SessionSource
from praisonaiagents import Agent

agent = Agent(name="ops", instructions="Alert humans about incidents.")
botos = BotOS(bots=[Bot("telegram", agent=agent)])

botos.configure_channels({
    "telegram": {"home_channel": "123456", "aliases": {"ops-alerts": "123456"}},
})

async def notify():
    src = SessionSource(platform="telegram", channel_id="123456")
    await botos.deliver("origin", "Build finished!", origin=src)

asyncio.run(notify())
```

The user triggers an alert; BotOS delivers the agent's message to the target channel.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Agent[🤖 Agent] --> BotOS[BotOS]
    BotOS --> Router[DeliveryRouter]
    Router --> Channel[📨 Channel]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Agent agent
    class BotOS,Router process
    class Channel output
```

## Quick Start

<Steps>
  <Step title="Simple Usage">
    Reply to where the request came from:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import asyncio
    from praisonai.bots import BotOS, Bot, SessionSource
    from praisonaiagents import Agent

    agent = Agent(name="ops", instructions="Send status updates.")
    botos = BotOS(bots=[Bot("telegram", agent=agent)])

    async def notify():
        src = SessionSource(platform="telegram", channel_id="123456")
        await botos.deliver("origin", "Build finished!", origin=src)

    asyncio.run(notify())
    ```
  </Step>

  <Step title="With Configuration">
    Set home channels and deliver by alias:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    botos.configure_channels({
        "telegram": {"home_channel": "123456", "aliases": {"ops-alerts": "123456"}},
    })

    await botos.deliver("telegram", "Nightly digest ready")
    await botos.deliver("ops-alerts", "Disk usage at 90%")
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant Agent
    participant BotOS
    participant Router as DeliveryRouter
    participant Channel

    Agent->>BotOS: deliver(target, text, origin?)
    BotOS->>Router: resolve(target)
    Router->>Channel: outbound message
```

| Target            | Resolves to                                                   |
| ----------------- | ------------------------------------------------------------- |
| `origin`          | Chat that triggered the request (`origin=SessionSource(...)`) |
| `<platform>`      | Platform home channel from `/sethome` or config               |
| `<platform>:<id>` | Explicit channel ID                                           |
| `<alias>`         | Friendly name from `configure_channels` or overlay file       |

Resolution order: `origin` → `platform:id` → bare platform → alias. Platform names win over aliases.

Home and observed channels persist to `~/.praisonai/state/channel_directory.json`.

***

## Configuration Options

### YAML

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
channels:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}
    home_channel: "123456"
    aliases:
      ops-alerts: "123456"
```

### Alias overlay

Pre-name channels in `~/.praisonai/state/channel_aliases.json`:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "engineering": { "platform": "discord", "channel_id": "555" },
  "ops": "slack:C111"
}
```

Call `botos.delivery_router.refresh_directory()` periodically so adapters enumerate channels the bot has not yet heard from.

***

## Rate Limiting

Scheduled and background sends share the reply-path rate limiter automatically — no config change required.

Scheduled sends also fire **at most once** per due window across multiple `BotOS` processes — see [BotOS → Multi-Process / HA Deployments](/docs/features/botos#multi-process-ha-deployments).

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Agent[🤖 Agent] --> Msg[BotOutboundMessenger]
    Msg --> Rate{⚖️ Rate Limiter}
    Rate --> Send[📤 Send text]
    Send --> Done[✅ Delivered]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class Agent agent
    class Msg,Rate,Send process
    class Done result
```

| You call                                            | What happens                                                                 |
| --------------------------------------------------- | ---------------------------------------------------------------------------- |
| `botos.deliver("ops-alerts", "...")`                | Rate-limited per platform via the shared reply-path rate limiter.            |
| `botos.deliver("ops-alerts", "...", origin=source)` | Rate-limited with origin context; used for targeted proactive replies.       |
| Adapter's `send_message()` returns `False`          | Treated as a transient failure. Dead-target flag not tripped. Safe to retry. |

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Scheduled daily digest — proactive send to a named channel
await botos.deliver(
    "ops-alerts",
    "Nightly digest ready",
)
```

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Targeted proactive send with origin context
await botos.deliver(
    "ops-alerts",
    "Build finished",
    origin=source,
)
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Prefer aliases over raw IDs">
    Aliases survive renumbering and read better in logs.
  </Accordion>

  <Accordion title="Set home_channel for each platform">
    Bare-platform targets fail without a configured home channel.
  </Accordion>

  <Accordion title="Do not name aliases after platforms">
    `telegram` as an alias will never resolve — platform lookup wins.
  </Accordion>

  <Accordion title="Handle the bool return">
    `deliver()` returns `False` on resolution or send failure — log and retry as needed.
  </Accordion>

  <Accordion title="Use durable delivery for cross-worker dedup">
    `BotOS.deliver` rate-limits sends via the shared reply-path rate limiter, but does not support idempotency deduplication. For dedup across retries or workers, use the reply-path `delivery.send(...)` outbox (see [Durable Delivery](/docs/features/durable-delivery)).
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="BotOS" icon="robot" href="/docs/features/botos">
    Multi-platform orchestration
  </Card>

  <Card title="Bot Gateway" icon="server" href="/docs/features/bot-gateway">
    Run multiple bots from one server
  </Card>
</CardGroup>
