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.POST /hooks/<path> endpoint on the gateway — point any external service (GitHub Actions, Linear, Gmail push, Sentry alerts) at it and the gateway runs an agent and delivers the reply to a configured channel.
/hooks/<path>; the gateway authenticates the payload, runs the mapped agent, and delivers the reply to the configured channel.
Quick Start
1
Define a hook and start the gateway
2
Point your external service at the hook URL
3
The agent replies to Telegram automatically
No polling, no webhook handler code — the gateway does it all.
How It Works
Three Ways to Register a Hook
- Python
- YAML (gateway.yaml)
- CLI
HookConfig Options
Templating
Both{{ payload.x }} (Jinja-style) and {x} (format-string style) are accepted:
- The leading
payload.is optional —{{ payload.from }}and{from}resolve identically. - Missing keys render as empty strings — the template never raises on partial payloads.
- Single-pass substitution — a payload value containing
{...}is never re-expanded (injection-safe).
Actions
"agent" (default) — runs a full agent turn with the rendered message as the user input:
"wake" — nudges an existing session (triggers proactive delivery or a scheduled check-in) without injecting a new user message:
Idempotency & Retries
The gateway deduplicates concurrent and retried deliveries:- When
idempotency_keyis set, the key is rendered from the payload and hashed asSHA-256(path + "\x00" + rendered_key). - When unset, the entire payload is JSON-canonicalized and hashed.
- The key is reserved in-flight atomically — concurrent identical POSTs dedup across the await boundary.
- The key is committed only after a successful agent run — transient failures remain retryable.
External services that retry on timeout (e.g. GitHub webhooks, Linear) are safe to point directly at inbound hooks without additional dedup logic on your side.
Security
Verifying Provider Signatures (HMAC)
Setsecret to have the gateway verify the provider’s HMAC signature over the raw request body — a missing or invalid signature is rejected with 401 before any agent runs.
Verification is fail-closed and opt-in: with no secret set, nothing changes. When secret is set without an explicit signature_header, the header defaults to X-Hub-Signature-256 (the GitHub/webhook convention) so a bare secret is a working config, not a 401 trap.
- GitHub (Python)
- GitHub (YAML)
- Stripe (YAML)
Event Filtering
Setevents to an allow-list so only matching deliveries run a turn — everything else is a cheap 200 {"ok": true, "skipped": "event"} with no LLM cost.
The event type is read from event_header (a request header) or, when that header is absent, from the payload as a dotted path (defaulting to "event").
issues) in the header and the sub-type in the payload’s action. A namespaced filter like issues.opened matches only when action == "opened" — fail-closed: a delivery that omits action is never admitted, so a bare issues event cannot slip through a filter that only allows issues.opened.
Read the event from a payload field instead of a header by pointing event_header at a dotted path:
Deliver-Only Mode (no LLM turn)
Setdeliver_only: true to route the rendered message straight to deliver_to — no agent, no LLM cost, sub-second forwarding.
deliver_only composes with signature verification and event filtering and requires deliver_to. Response shapes:
End-to-End Example: Gmail → Triage Agent → Telegram
- Gmail push subscription fires
POST /hooks/gmailwith the email payload. - Gateway verifies the bearer token from
$GMAIL_HOOK_SECRET. - Session key
gmail:<message_id>scopes the conversation to this email thread. - The rendered message is sent to the
email-triageragent. - The agent’s reply is delivered to Telegram chat
123456789.
Best Practices
Always set an idempotency_key for event-driven sources
Always set an idempotency_key for event-driven sources
External webhooks retry on timeout. Without an
idempotency_key, a slow agent run followed by a timeout retry will run the agent twice. Use a message or event id from the payload.Use per-hook auth tokens, not the global token
Use per-hook auth tokens, not the global token
Set a distinct
auth secret per hook so you can rotate individual secrets without restarting the gateway or changing the global token.Set session_key to scope conversations
Set session_key to scope conversations
Without
session_key, all deliveries to a hook share the same session (hook:<path>). Use a payload field like {user_id} or {message_id} to isolate conversations by sender or thread.Test locally with curl before connecting a real service
Test locally with curl before connecting a real service
Related
Webhook Verification
HMAC signature verification for outbound bot webhooks (different surface)
Proactive Delivery
Delivery routing — the channel:target format used in deliver_to
Gateway Overview
Gateway configuration, channels, and multi-bot mode
Gateway CLI
All gateway CLI commands including hooks add / list / remove

