Shared Webhook Ingress

Serve every webhook channel behind one gateway port at /webhooks/<channel>

Every mode: webhook channel is served through the gateway’s single listener at /webhooks/<channel> — one port, one public URL, routed by path.

Diagram

Shared Webhook Ingress

/webhooks/whatsapp

/webhooks/billing_hook

/webhooks/stripe

🌐 WhatsApp

🚪 Gateway :8765

🌐 Billing hook

🌐 Stripe

🧭 Route by path

🤖 Agent

🤖 Agent

🤖 Agent

Quick Start

  1. Declare webhook channels — no port on any

    Leave webhook_port unset and every mode: webhook channel shares gateway.port.

    yaml
    # gateway.yaml
    gateway:
      port: 8765                  # the only public endpoint
    
    channels:
      whatsapp:
        platform: whatsapp
        mode: webhook             # served at /webhooks/whatsapp
      billing_hook:
        platform: webhook
        mode: webhook             # served at /webhooks/billing_hook
    bash
    praisonai gateway start --config gateway.yaml
  2. Opt out — pin one channel to its own port

    Set webhook_port on a channel to restore a standalone server for just that channel.

    yaml
    # gateway.yaml
    channels:
      legacy_hook:
        platform: webhook
        mode: webhook
        webhook_port: 9090        # standalone server on :9090 (opt-out)

How It Works

An external service POSTs to /webhooks/<channel>; the gateway verifies the signature per-channel, then dispatches to the agent.

Diagram
Agent🔐 Channel verifier🚪 Gateway :8765🌐 External serviceAgent🔐 Channel verifier🚪 Gateway :8765🌐 External servicealt[Signature invalid][Unknown path][Signature valid]POST /webhooks/whatsappverify(headers, raw_body)False401 Invalid signature404 Not foundTruerunreply200 OK

Each channel keeps its own verifier — a bad signature on one channel returns 401 without touching the others. An unknown path returns 404 with no cross-channel fallthrough. Hot-reloading a channel re-mounts /webhooks/<channel> automatically; removing a channel clears its mount — no process restart.

When do I set webhook_port?

Diagram

No — one URL is simpler

Legacy tunnel pinned to one port

Process isolation for one channel

Multiple webhook channels?

Need standalone isolation?

Leave webhook_port unset
(shared listener, default)

Set webhook_port
(standalone server)


Configuration Options

webhook_port selects shared vs. standalone mode on any mode: webhook channel.

OptionTypeDefaultDescription
webhook_portOptional[int]NoneNone → served through the gateway’s shared listener at /webhooks/<channel>. An int → binds a private standalone server (opt-out, backward-compatible)

Common Patterns

Multi-provider on one URL

Point GitHub, Stripe, and a generic billing hook at one public URL — the path picks the channel.

yaml
# gateway.yaml
gateway:
  port: 8765

channels:
  github:
    platform: webhook
    mode: webhook             # /webhooks/github
  stripe:
    platform: webhook
    mode: webhook             # /webhooks/stripe
  billing_hook:
    platform: webhook
    mode: webhook             # /webhooks/billing_hook

Provider dashboards get https://bots.example.com/webhooks/github, .../webhooks/stripe, .../webhooks/billing_hook.

Reverse-proxy termination for one HTTPS URL

Terminate TLS at your proxy and forward one path prefix to the gateway.

nginx
# nginx
location /webhooks/ {
    proxy_pass http://127.0.0.1:8765;
}

Explicit-port opt-out for a legacy tunnel

A tunnel already pinned to a fixed port keeps working — set webhook_port on that one channel.

yaml
# gateway.yaml
channels:
  legacy_hook:
    platform: webhook
    mode: webhook
    webhook_port: 8080        # standalone server for a pinned tunnel

Best Practices

Prefer the shared listener

One public URL is simpler to secure, monitor, and put behind a reverse proxy. Leave webhook_port unset unless you have a specific reason not to.

Set an HMAC secret per channel

The gateway delegates verification to each channel’s own verifier, fail-closed — a bad signature returns 401. Give every webhook channel its own verify.hmac.secret.

Don't set webhook_port unless you need isolation

An explicit port opts a channel out of the shared listener and back onto a standalone server. Use it only for a legacy tunnel pinned to one port, or when one channel needs process isolation.

Rely on automatic re-mount after hot-reload

After a hot-reload, /webhooks/<channel> re-mounts automatically and a removed channel’s mount is cleared — no restart required.


Related