Skip to main content
The gateway now ships in the praisonai-bot package. praisonai serve gateway still works exactly as documented here; for a standalone install see praisonai-bot Migration.
Trigger an agent run from any external HTTP event — Gmail, GitHub, Stripe, CI, forms, IoT — by POSTing JSON to /hooks/<path>.
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Summarise inbound hook payloads for the team.")
agent.start("Process the Gmail hook payload and post a short summary.")
The user POSTs JSON to /hooks/<path>; the gateway verifies auth, runs the mapped agent, and delivers the reply on the configured channel.

Quick Start

1

YAML — simplest form

Create gateway.yaml:
agents:
  assistant:
    instructions: "You are a helpful email assistant."

hooks:
  - path: gmail                                      # POST /hooks/gmail
    agent: assistant
    auth: "${GATEWAY_HOOK_TOKEN}"
    session_key: "hook:gmail:{{ payload.message_id }}"
    idempotency_key: "{{ payload.message_id }}"
    deliver_to: "telegram:123456789"
    message: "New email from {{ payload.from }}: {{ payload.subject }}"
Start the gateway:
praisonai gateway start --config gateway.yaml
Fire a test event:
curl -X POST http://localhost:8765/hooks/gmail \
  -H "Authorization: Bearer $GATEWAY_HOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message_id":"abc","from":"alice@example.com","subject":"Hello"}'
Response:
{"ok": true, "session_key": "hook:gmail:abc"}
2

Python — register programmatically

from praisonaiagents import Agent
from praisonai.gateway import WebSocketGateway

gateway = WebSocketGateway()
gateway.register_agent("assistant", Agent(
    name="Assistant",
    instructions="You are a helpful email assistant.",
))
gateway.register_hook(
    path="gmail",
    agent="assistant",
    session_key="hook:gmail:{message_id}",
    idempotency_key="{message_id}",
    deliver_to="telegram:123456789",
    message_template="New email from {from}: {subject}",
)
3

CLI — manage hooks at runtime

praisonai gateway hooks add gmail \
  --agent assistant \
  --session-key "hook:gmail:{message_id}" \
  --deliver-to telegram:123456789 \
  --message "New email from {from}: {subject}"

praisonai gateway hooks list
praisonai gateway hooks remove gmail

How It Works

ConceptYAML keyPython kwargCLI flag
URL segmentpathpath=positional
Agent to runagentagent=--agent
Action modeactionaction=--action
Bearer secretauthauth=--auth
Session id templatesession_keysession_key=--session-key
Dedup key templateidempotency_keyidempotency_key=--idempotency-key
Delivery targetdeliver_todeliver_to=--deliver-to
Agent message templatemessagemessage_template=--message
Active?enabledenabled=

Two Actions: agent vs wake

Choose based on whether the external event carries new content for the agent. action: agent (default) — runs a full agent turn on the templated message. Use when the external event carries new content the agent should process. action: wake — nudges an existing session’s _last_activity without a new user message. Use when the external event means “this session is still alive / re-deliver any pending work”.

Templating

Both placeholder styles work and render the same payload fields:
StyleExampleUsed in
{{ payload.x }}{{ payload.message_id }}YAML examples
{x}{message_id}Python / CLI examples
Rules:
  • A leading payload. is optional — {{ payload.from }} and {{ from }} both work.
  • Dotted paths resolve nested keys: {{ payload.user.email }}{user: {email: "x"}}.
  • Missing keys render as empty strings — templates never raise.
  • Substitution is single-pass — payload values containing {...} are not re-expanded (prevents key-corruption via payload injection).
Worked example: Payload:
{"message_id": "abc123", "from": "alice@example.com", "subject": "Hello"}
Template config:
session_key: "hook:gmail:{{ payload.message_id }}"
idempotency_key: "{{ payload.message_id }}"
message: "New email from {{ payload.from }}: {{ payload.subject }}"
Resolved values:
  • session_key"hook:gmail:abc123"
  • idempotency_key"abc123"
  • message"New email from alice@example.com: Hello"

Idempotency & Retries

  • idempotency_key is a template; the rendered value is hashed (sha256) and scoped by path so the same id on different hooks never collide.
  • If omitted, the entire payload is hashed deterministically (canonical JSON).
  • Store is bounded (10,000 entries) with a 24h TTL — pruned lazily on each delivery.
  • Key is only recorded on success — transient failures stay retryable.
  • Concurrent identical deliveries are deduplicated atomically (in-flight reservation prevents TOCTOU between seen-check and record).
  • Duplicate response: 200 {"ok": true, "deduplicated": true}.

Authentication

  • Per-hook bearer: set auth: to a literal token or ${ENV_VAR} in YAML.
  • Falls back to the gateway’s auth_token when no hook-specific secret is set.
  • Bearer header only?token= query params are rejected by design (prevents secret leakage into access logs).
  • Compared in constant time (secrets.compare_digest).
  • 401 if no token provided, 403 if token is wrong.
hooks:
  - path: github
    auth: "${GITHUB_WEBHOOK_SECRET}"    # per-hook secret
    agent: triage

Delivery

  • deliver_to: "channel:target" — e.g. "telegram:123456789", "discord:987654321", "slack:U12345".
  • Reuses the same channel-bot send path as scheduled delivery (hooks and scheduler route outbound identically).
  • If the channel bot is not registered, delivery is logged as failed and the hook returns {"ok": false} so the sender retries.
  • Omit deliver_to to skip outbound delivery entirely — the agent still runs.

Config Locations & Hot-Reload

YAML hooks can live at the top level (hooks:) or nested under gateway: (for grouping with other gateway settings):
# Top-level (recommended)
hooks:
  - path: gmail
    agent: assistant

# Or nested under gateway:
gateway:
  host: "0.0.0.0"
hooks:
  - path: gmail
    agent: assistant
Both locations are picked up at startup and at reload_config — a config reload clears and re-registers the entire hook table, so removed hooks and rotated secrets take effect without a process restart. See Gateway Hot-Reload.

Real User-Interaction Flow

Gmail received a new email. Zapier POSTs the parsed message to /hooks/gmail. The gateway deduplicates by message_id, runs the assistant agent on a templated summary, and Telegram chat 123456789 gets a notification with the agent’s reply. If Zapier retries the same delivery, the gateway returns {"ok": true, "deduplicated": true} instantly without re-running the agent.

Common Patterns

GitHub Issue Triage

hooks:
  - path: github/issue
    agent: triage
    auth: "${GITHUB_WEBHOOK_SECRET}"
    session_key: "gh:issue:{{ payload.issue.number }}"
    idempotency_key: "{{ payload.delivery }}"
    deliver_to: "slack:#triage"
    message: "New issue #{{ payload.issue.number }}: {{ payload.issue.title }}"
Webhook POSTs arrive, the triage agent classifies the issue, and a Slack notification lands in #triage.

Stripe Payment Event

hooks:
  - path: stripe/payment
    agent: billing-support
    action: wake                          # event has no new content — just re-activate
    auth: "${STRIPE_WEBHOOK_SECRET}"
    session_key: "billing:{{ payload.data.object.customer }}"
    idempotency_key: "{{ payload.id }}"
Payment events nudge the billing-support session for that customer without generating a new agent turn.

CI Failure Ping

hooks:
  - path: ci/failure
    agent: ops
    auth: "${CI_WEBHOOK_TOKEN}"
    session_key: "ci:{{ payload.repo }}:{{ payload.branch }}"
    idempotency_key: "{{ payload.run_id }}"
    deliver_to: "telegram:${OPS_CHAT_ID}"
    message: "CI failed on {{ payload.repo }}/{{ payload.branch }} — {{ payload.url }}"
The ops agent investigates and the reply goes directly to the on-call Telegram chat.

Best Practices

Use the provider’s native delivery id (Gmail message_id, GitHub X-GitHub-Delivery, Stripe event id). Never rely on time of receipt — providers retry with identical ids.
A separate auth: token per integration means a leaked secret isolates to one source, not your entire webhook surface.
Derive the session key from a stable entity id (customer, repo, user) so related events thread through the same conversation and the agent has full context.
If the webhook just signals “still alive” or “payment completed” without carrying content the agent should read, action: wake saves an LLM call.

Gateway Overview

Gateway architecture and how channels, agents, and routing connect.

Gateway CLI

Full CLI reference including the praisonai gateway hooks subcommand.

Bot Lifecycle Hooks

In-process outbound hooks (GATEWAY_START, SESSION_START, SCHEDULE_TRIGGER) — the counterpart to inbound HTTP triggers.

Gateway Hot-Reload

How hook changes and rotated secrets take effect without a process restart.