Skip to main content
Bot platform adapters now ship in the praisonai-bot package. praisonai bot serve still works exactly as documented here; for a standalone install see praisonai-bot Migration.
Bot lifecycle hooks let you watch your bot start and stop, see every user session begin and end, and react when scheduled jobs fire — without changing your agent code.
This page covers in-process outbound lifecycle hooks (GATEWAY_START, SESSION_START, SCHEDULE_TRIGGER) that fire inside the running gateway. For HTTP inbound triggers that let external services start agent runs via POST /hooks/<path>, see Gateway Inbound Hooks.
The user opens a chat session; lifecycle hooks fire on gateway start, session boundaries, and scheduled jobs.

Quick Start

1

Register a SESSION_START hook

2

Pass the registry to your agent and bot

Running this bot now prints a line whenever a user opens a new session.

How It Works

BEFORE_AGENT and AFTER_AGENT are fired by agent.chat() itself — the gateway does not re-fire them to avoid double-dispatch to plugins.

Events Fired by the Bot Runtime

SESSION_END reason values:
  • clear — user sent /new
  • policy — policy auto-reset triggered
  • stale — session reaped due to inactivity
  • clear_allreset_all called (clears all sessions)

Common Patterns

Audit Log

Log every gateway and session event to a file with timestamps.

Per-User Usage Counter

Increment a counter when a session starts, persist it when the session ends.

Scheduled-Job Observability

Push a metric every time a scheduled job fires.

Configuration / HookResult

Two events that DO gate: MESSAGE_RECEIVED and MESSAGE_SENDING are real policy control points — HookResult.deny("reason") drops the message and the agent is never invoked; HookResult(decision="allow", modified_input={"content": "..."}) rewrites the content before the agent sees it. Returning HookResult.deny("reason") from gateway or session lifecycle hooks is best-effort for those specific events (GATEWAY_START, GATEWAY_STOP, SESSION_START, SESSION_END): BotOS emits them but does not gate startup or shutdown on the result.
MESSAGE_RECEIVED is now an inbound gate, symmetric with MESSAGE_SENDING on the outbound side. A hook can drop (deny) or redact (rewrite content via modified_input) an inbound message before agent dispatch. This works consistently across sync and async adapters (Telegram, Slack, Discord, WhatsApp, Email, AgentMail) — no async def required. See Hook Events → Message Events.
For policy enforcement on agent internals (e.g. blocking tool calls or LLM requests), use BEFORE_TOOL or BEFORE_LLM hooks.
Exception — MESSAGE_RECEIVED is now a real gate. Since PR #2589, returning HookResult.deny(...) from MESSAGE_RECEIVED drops the inbound message, and HookResult(decision="allow", modified_input={"content": "…"}) rewrites it in place before the agent sees it. See Inbound Message Gate.

Best Practices

Gateway and session hooks may run inside an async event loop. Avoid blocking I/O or heavy computation — use fire-and-forget coroutines or thread pools for slow operations.
Emission is wrapped in try/except inside the bot runtime, but unhandled exceptions from your hook function are logged at debug level and swallowed. Return HookResult.allow() even on internal errors to avoid silent failures.
When one BotOS runs multiple bots (each with a different agent), the agent_name field on every event identifies which agent the event belongs to. Key your per-agent metrics on event_data.agent_name.
Platform user IDs differ between Telegram, Discord, Slack, and WhatsApp. Use session_id from SessionStartInput / SessionEndInput as the stable key for per-user state across platforms.

Inbound Message Gate

Drop or redact incoming messages before the agent sees them

Hook Events Reference

Complete event reference with all input types and fields

BotOS

Multi-platform bot orchestrator that emits these lifecycle hooks

Hooks

Hook system concepts: registries, decisions, and matchers

Session Management

How per-user sessions are managed across platforms