Skip to main content
The heavy gateway implementation (WebSocketGateway) lives in the praisonai_bot.gateway package. praisonaiagents.gateway holds only protocols and lightweight utilities. praisonai serve gateway and from praisonai.gateway import WebSocketGateway still work exactly as documented here via re-export shims; for a standalone install see praisonai-bot Migration.
Agents can connect through a unified gateway providing single-entry access to multi-agent coordination, tools, and streaming events.

Quick Start

1

Simple Agent Gateway

Deploy an agent through the unified gateway:
from praisonaiagents import Agent

# Gateway enables unified access
agent = Agent(
    name="Gateway Agent",
    instructions="Connect through unified gateway",
    gateway=True
)

agent.start("Process through gateway")
2

Multi-Service Gateway

Configure agents with full gateway capabilities:
from praisonaiagents import Agent, GatewayConfig

agent = Agent(
    name="Gateway Agent",
    instructions="Multi-service coordination",
    gateway=GatewayConfig(
        unified=True,
        services=["agents", "mcp", "a2a", "a2u"]
    )
)

How It Works

Agents connect to services through gateway routing:
ComponentPurposeAgent Access
GatewaySingle entry pointgateway=True
UnifiedAll services combinedDefault mode
ServicesIndependent scalingService-specific
For bot gateways, use the one-line reliability="production" preset on BotOS, gateway YAML, or praisonai gateway start --reliability production instead of tuning drain and admission separately. See Gateway Reliability.

Hot Reload

Edit gateway.yaml while the gateway is running — file watch (with optional watchdog) and SIGHUP share the same diff-driven reload path, with drain-coordinated channel restarts. Rotating gateway.auth_token in the same reload also revokes any live sessions on the old secret — see Gateway Credential Rotation. See Gateway Config Reload. For proactive/scheduled outbound delivery routing — including friendly alias targets like "family" or "ops" — see Friendly Aliases for Scheduled Delivery.

Voice notes

Voice messages sent to your gateway bot are transcribed automatically on Telegram, Slack, and WhatsApp — no code changes required. Configure the stt: block under each channel to force a language or opt out. See Voice Notes (Speech-to-Text).

Authentication

Authentication posture changes automatically based on the bind interface.
InterfaceModeAuth Required
Loopback (127.0.0.1, localhost, ::1)PermissiveNo
External (0.0.0.0, LAN IPs, public IPs)StrictYes

Bind-Aware Authentication

Complete authentication security guide

Rotating the shared secret

Change gateway.auth_token in the running gateway.yaml and hot-reload — every already-connected session that authenticated under the old secret is force-closed with WebSocket close code 4001 and reason credentials_rotated. New connections use the new secret. Defaults on; opt out with gateway.revoke_on_secret_rotation: false. See Gateway Credential Rotation for the full behaviour matrix. The auto-generated per-install HMAC secret for pairing codes (.gateway_secret) is stored and permission-remediated separately — see Bot Pairing → Secret Management.

Pre-auth edge protections

Two guards protect internet-exposed gateway deployments by default: a per-IP connection budget (preauth_max_connections_per_ip=32) that caps unauthenticated WebSocket slots before auth runs, and a per-connection flood guard (max_unauthorized_frames=10) that closes connections sending too many unauthorized frames. Loopback clients are always exempt. Both are disabled by setting the option to 0.

Gateway Edge Protections

Per-IP connection budget, unauthorized-frame flood guard, and WebSocket close codes 4028 / 4029

Handshake and Version Negotiation

New WebSocket clients should send a hello frame to negotiate protocol version, capabilities, and policy limits in one round trip. The legacy joinjoined flow remains supported for existing clients.

Gateway Handshake Protocol

Version negotiation, capabilities, and structured connection errors

Configuration Options

Gateway Configuration

Python gateway configuration options
ServiceDefault PortProtocolAgent Access
unified8765HTTP + WSDefault gateway
agents8000HTTP/RESTDirect API calls
mcp8080HTTP/SSETool protocols
a2a8001JSON-RPCAgent communication
a2u8002SSEEvent streams
openai8765HTTP + SSEOpenAI API compatibility
Edge protection fields (available on GatewayConfig and gateway.yaml):
OptionTypeDefaultDescription
preauth_max_connections_per_ipint32Max concurrent unauthenticated WS connections per source IP. 0 disables. Loopback exempt.
max_unauthorized_framesint10Close the connection after N unauthorized frames. 0 disables.
revoke_on_secret_rotationbooltrueForce-close live sessions when auth_token is rotated via config reload. false adopts the new secret for new connections only. See Gateway Credential Rotation.
api.openaiboolfalseWhen on, mounts /v1/chat/completions, /v1/responses, /v1/models on the same port and auth. See Gateway API Endpoints.
api.mcpboolfalseWhen on, mounts an MCP JSON-RPC endpoint at /mcp on the same port and auth. See Gateway API Endpoints.

Gateway YAML Configuration

The gateway.yaml schema accepts three optional top-level blocks alongside channels and agents. All three are optional and ignored when not present.

gateway: block

Global gateway settings — reliability preset, drain timeout, health monitor:
gateway:
  reliability: production    # "production" | "default" | "off"
  drain_timeout: 15          # seconds to wait for in-flight turns on shutdown

channels:
  telegram:
    platform: telegram
    token: ${TELEGRAM_BOT_TOKEN}
    stt:
      enabled: true          # transcribe inbound voice notes (default on)
      echo_transcripts: true  # echo the recognised text back to the user
  discord:
    platform: discord
    token: ${DISCORD_BOT_TOKEN}
Inbound voice notes on Telegram, Slack, and WhatsApp are transcribed automatically — see Voice Notes.
Discord and Slack channels are auto-discovered on every refresh — your bot can address a channel before anyone messages it. See Channel Directory.

api: block

Serve OpenAI-compatible and MCP HTTP endpoints from the same gateway process, sharing the live agents and sessions that chat users reach:
gateway:
  api:
    openai: true    # mounts /v1/chat/completions, /v1/responses, /v1/models
    mcp: true       # mounts /mcp (JSON-RPC)
Both surfaces are opt-in and off by default. See Gateway API Endpoints for the endpoint list, auth, and client examples.

hooks: block

Register shell-command hooks that fire at gateway and message lifecycle events:
hooks:
  - event: message_received
    command: /usr/local/bin/rate-check.sh
  - event: gateway_start
    command: "curl -s https://monitoring.example.com/ping"

BotOS.from_config and tool names

BotOS.from_config("botos.yaml") now routes tool names through the standard ToolResolver chain:
  1. Local tools.py in the working directory
  2. Wrapper ToolRegistry
  3. praisonaiagents built-in tools
  4. praisonai-tools package
  5. Installed plugins
This means a YAML bot referencing a praisonai-tools symbol or a local tools.py function just works — no ad-hoc importlib lookup required:
# botos.yaml
agent:
  name: assistant
  instructions: You are a helpful assistant.
  tools:
    - web_search          # resolved via praisonai-tools
    - my_custom_tool      # resolved from local tools.py
platforms:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}
from praisonai.bots import BotOS

botos = BotOS.from_config("botos.yaml")
botos.run()

Gateway Agent Defaults

Gateway agents loaded from YAML use chat-optimised defaults that differ from the Python SDK.
YAML keyGateway defaultWhy
reflectionfalseChat channels need sub-second replies; self-reflection adds ~8x latency on short prompts
tool_choicenull (auto)Let the LLM decide when to call tools
allow_delegationfalsePrevents cross-agent routing unless explicitly opted in
agents:
  assistant:
    instructions: "You are a helpful AI assistant."
    model: gpt-4o-mini
    reflection: true   # opt-in: enables self-critique, ~8x slower
Changed in PraisonAI v4.6.26: gateway agents now default to reflection: false. Previous versions defaulted to true. See PR #1485.

Per-Agent Approval Isolation

“Allow always” grants persist across gateway restart and default to being scoped to the approving agent, so one agent’s approval never authorises another. Grants live in a durable SQLite store at ~/.praisonai/state/gateway/approvals.sqlite.

Gateway Scoped Approvals

Durable, agent-scoped allow-always grants, resolver scoping options, and the /api/approval/allow-list endpoint
Gateway also supports isolated exec approval managers for multi-tenant environments where agents must not share approval state.

Default vs Per-Agent Managers

Manager TypeUse CaseApproval Sharing
DefaultCLI, single-agent setupsShared process-wide
Per-AgentMulti-tenant gatewaysIsolated per instance
from praisonai.gateway.exec_approval import (
    get_default_exec_approval_manager,
    create_exec_approval_manager
)

# Default manager - shared across all agents
default_manager = get_default_exec_approval_manager(ttl=300.0)

# Per-agent manager - isolated approval state
agent1_manager = create_exec_approval_manager(ttl=120.0)
agent2_manager = create_exec_approval_manager(ttl=180.0)

Multi-Agent Gateway with Isolation

from praisonaiagents import Agent
from praisonai.gateway.exec_approval import create_exec_approval_manager

class IsolatedGateway:
    def __init__(self):
        self.agents = {}
        
    def add_agent(self, name, instructions, approval_ttl=300):
        # Each agent gets isolated approval manager
        manager = create_exec_approval_manager(ttl=approval_ttl)
        
        agent = Agent(
            name=name,
            instructions=instructions,
            exec_approval_manager=manager,
            gateway=True
        )
        
        self.agents[name] = {"agent": agent, "manager": manager}
        return agent
        
    def get_pending_approvals(self, agent_name):
        if agent_name in self.agents:
            manager = self.agents[agent_name]["manager"]
            return manager.list_pending()
        return []

# Usage
gateway = IsolatedGateway()

# Tenant A agent
tenant_a = gateway.add_agent(
    "TenantA_Assistant", 
    "Help tenant A users",
    approval_ttl=180  # 3 minutes
)

# Tenant B agent  
tenant_b = gateway.add_agent(
    "TenantB_Assistant",
    "Help tenant B users", 
    approval_ttl=600  # 10 minutes
)

# Isolated approval queues
pending_a = gateway.get_pending_approvals("TenantA_Assistant")
pending_b = gateway.get_pending_approvals("TenantB_Assistant")
The ttl parameter controls how long approval requests wait before auto-expiring (default: 5 minutes). The get_exec_approval_manager() function is preserved as a backward-compatible alias for get_default_exec_approval_manager().

Allow-list endpoint

GET/POST/DELETE /api/approval/allow-list manages allow-always grants. POST and DELETE accept an optional agent_id; GET returns a grants view alongside the legacy allow_list.
{
  "allow_list": ["read_file"],
  "grants": [
    {"agent_id": "shell-bot", "tool_name": "shell_exec", "arg_signature": null, "created_at": 1720000000.0, "approver": "gateway:human"}
  ]
}
Omitting agent_id on POST/DELETE targets a legacy any-agent grant; supplying it scopes to that agent. See Gateway Scoped Approvals for full curl examples.

Gateway lifecycle predicates

Core SDK exports three pure predicates for gateway lifecycle decisions. Each is side-effect free and testable in isolation; the wrapper owns the actual teardown.
SymbolPurposeDocumentation
ScaleToZeroPolicyQuiesce when idle; wake on next inboundScale to Zero
DrainTimeoutPolicyBounded wait for in-flight turns on shutdownSession Continuity
DrainMarkerPolicy + current_epoch()Port-less, restart-safe external drain signalDrain Trigger
classify_exit_reason + FatalConfigErrorSupervisor-friendly sysexits exit codes (0 / 75 / 78)Gateway Exit Codes
from praisonaiagents.gateway import (
    ScaleToZeroPolicy,
    DrainTimeoutPolicy,
    DrainMarkerPolicy,
    current_epoch,
)

Kanban Dispatcher

Kanban dispatcher (v4.6.x): Worker file descriptors are now released as soon as the subprocess starts (fixes a slow leak under sustained dispatch). release_claim failures are logged with full stack traces instead of being silently swallowed — orphaned claimed tasks now leave a clear log trail for triage. KeyboardInterrupt / SystemExit / asyncio cancellation propagate cleanly through dispatcher shutdown.

Common Patterns

Single Gateway Deployment

from praisonaiagents import Agent

# Simple unified gateway
agent = Agent(
    name="Gateway Agent",
    gateway=True,  # Enables unified gateway
)

# CLI deployment
# praisonai serve unified --port 8765

Multi-Agent Gateway

from praisonaiagents import Agent, Task, PraisonAIAgents

agents = [
    Agent(name="Researcher", gateway=True),
    Agent(name="Writer", gateway=True),
]

# Multi-agent coordination through gateway
tasks = [Task(description="Research topic", agent=agents[0])]
crew = PraisonAIAgents(agents=agents, tasks=tasks)

Development Gateway

from praisonaiagents import Agent

agent = Agent(
    name="Dev Agent",
    gateway=True,
    debug=True  # Development features
)

# CLI: praisonai serve unified --reload

Best Practices

Use praisonai serve unified for simplicity. Agents connect automatically without configuration.
Enable --reload for development. Use separate services for production scaling.
All servers expose /__praisonai__/discovery for endpoint discovery. Agents can auto-discover capabilities.
Reserve port 8765 for unified gateway. Use default ports for service-specific deployments.

Exit Codes & Supervisor Integration

praisonai serve gateway uses sysexits-based exit codes so process supervisors can distinguish a transient failure (restart) from a fatal misconfiguration (stop and fix).
CodeConstantMeaningSupervisor action
0GATEWAY_OK_EXIT_CODEClean shutdown / successNo restart needed
75GATEWAY_RESTART_EXIT_CODE (EX_TEMPFAIL)Transient failure — network blip, recoverable upstream errorRestart
78GATEWAY_FATAL_CONFIG_EXIT_CODE (EX_CONFIG)Fatal misconfiguration — fix before restartingDo not restart

What Triggers Fatal (78)

CauseExit code
Missing or malformed gateway.yaml (schema invalid / ValueError)78
Missing or malformed --agents file78
Missing required dependencies78
Any FatalConfigError raised explicitly78
Network blip at startup75
Recoverable platform error75

systemd Unit

[Unit]
Description=PraisonAI Gateway
After=network.target

[Service]
ExecStart=/usr/local/bin/praisonai serve gateway
Restart=on-failure
RestartSec=5

# Stop the crash-loop on fatal config — fix the config and start manually
RestartPreventExitStatus=78

[Install]
WantedBy=multi-user.target

Kubernetes Restart Policy

apiVersion: v1
kind: Pod
spec:
  containers:
    - name: praisonai-gateway
      image: praisonai:latest
      command: ["praisonai", "serve", "gateway"]
  restartPolicy: OnFailure
For Kubernetes, use a wrapper script that maps exit code 78 to success (exit 0) to prevent the pod from restarting on fatal config:
#!/bin/sh
praisonai serve gateway "$@"
code=$?
if [ $code -eq 78 ]; then
  echo "Fatal config error — not restarting (code 78)" >&2
  exit 0
fi
exit $code

In-Place Code Updates and /model

When a running gateway’s code changes on disk (via git pull, pip install -U, or an auto-update on a durable volume), the first-use lazy import triggered by a /model switch can hit a code mismatch and crash with a cryptic ImportError. The code-skew guard refuses with a clear message instead.

How It Works

At startup, the gateway captures a fingerprint of the installed code (git SHA + newest .py mtime). Before each /model switch it compares this to the current on-disk fingerprint:

User-Facing Message

When skew is detected, the user sees:
⚠️ Gateway code changed on disk since it started (<boot-short> → <disk-short>). Restart the gateway to apply updates before switching models.
Where <boot-short> and <disk-short> are 7-character shortened fingerprints (e.g. git SHAs or mtime: suffixes).

Fail-Open

The guard is best-effort and fail-open: if the fingerprint cannot be determined (no git, unreadable directory), the guard returns None and the /model switch proceeds normally. Normal operation is never blocked by the guard itself.

Opt-Out

Disable the guard for a session manager:
session_manager.code_skew_guard = False

Agents

Core agent functionality

MCP Protocol

Model Context Protocol integration

Gateway CLI

CLI commands for managing the gateway

Gateway API Endpoints

OpenAI-compat and MCP HTTP endpoints on the live gateway

Relay Transport

Out-of-process platform connector relay

Bot Chat Commands

Built-in and custom slash commands for gateway bots

Slash Command Menu

Native / autocomplete for Telegram and Discord bots

Voice Notes

Automatic transcription of inbound voice messages

Run Status Controller

Transport-agnostic run-progress state machine with a stall watchdog