📑 Table of contents

Hermes Agent: Webhooks — Triggering Hermes from Outside

Hermes Agent 🔴 Advanced ⏱️ 11 min read 📅 2026-05-05

Introduction

Hermes Agent is more than a conversational assistant — it''s also an automation engine capable of reacting to external events. Webhooks are the bridge between the outside world and your Hermes instance. Whether it''s a Pull Request on GitHub, a monitoring alert, a form submission, or a CI/CD pipeline completion, webhooks allow Hermes to receive these signals and respond intelligently.

This article provides an in-depth exploration of Hermes'' integrated webhook system — from configuration in config.yaml to security mechanisms, concrete use cases, and troubleshooting best practices. This is an advanced guide that assumes familiarity with Hermes Agent fundamentals, covered in the introduction article and advanced configuration.

What Are Webhooks in Hermes?

A webhook is a mechanism where an external service sends an HTTP POST request to a URL you expose, containing a JSON payload describing an event. In Hermes, the Webhook adapter (gateway/platforms/webhook.py) is a platform adapter just like Telegram or Discord: it runs inside the multi-platform gateway and benefits from the same lifecycle, session management, and response delivery mechanisms.

Specifically, Hermes starts an HTTP server (aiohttp) on a configurable port (default 8644) and listens for POST requests on /webhooks/{route_name}. Each route is declared in config.yaml and defines which events to accept, how to transform the payload into an agent prompt, which skills to load, and where to deliver the response.

Webhook Architecture in Hermes

External service (GitHub, GitLab, Sentry…)
    │
    ▼  HTTP POST
┌─────────────────────────────┐
│  WebhookAdapter (aiohttp)    │
│  ┌─ HMAC validation          │
│  ├─ Rate limiting            │
│  ├─ Idempotency cache        │
│  ├─ Event filtering          │
│  ├─ Prompt template render   │
│  ├─ Skill injection          │
│  └─ Agent run (202 Accepted) │
└─────────────────────────────┘
    │
    ▼
Response delivered to:
  • Telegram / Discord / Slack (cross-platform)
  • GitHub PR comment
  • Internal log

The key point: the HTTP response is immediate (202 Accepted), the agent processes the request asynchronously, and the response is routed to the platform of your choice.

Configuration in config.yaml

Webhook configuration lives under the platforms.webhook key in your ~/.hermes/config.yaml. Here''s the full structure:

platforms:
  webhook:
    enabled: true
    extra:
      host: "0.0.0.0"
      port: 8644
      secret: "your-global-hmac-secret"
      rate_limit: 30
      max_body_bytes: 1048576
      routes:
        github_pr_review:
          secret: "${WEBHOOK_GITHUB_SECRET}"
          events:
            - pull_request
            - pull_request_review
          prompt: >
            Review this pull request and provide detailed feedback:
            PR #{pull_request.number} — {pull_request.title}
            Author: {pull_request.user.login}
            Body: {pull_request.body}
            Diff: {__raw__}
          skills:
            - code-review
          deliver: github_comment
          deliver_extra:
            repo: "{repository.full_name}"
            pr_number: "{pull_request.number}"

        monitoring_alert:
          secret: "${WEBHOOK_GRAFANA_SECRET}"
          events:
            - alerting
          prompt: >
            Analyze this monitoring alert and suggest actions:
            {__raw__}
          deliver: telegram
          deliver_extra:
            chat_id: "-1001234567890"

        deploy_notification:
          secret: "${WEBHOOK_DEPLOY_SECRET}"
          events:
            - deploy.success
            - deploy.failure
          prompt: >
            Deployment {event_type} on {environment}:
            {__raw__}
          deliver_only: true
          deliver: discord
          deliver_extra:
            chat_id: "123456789"

Global Parameters

  • host: listen address (default 0.0.0.0)
  • port: HTTP port (default 8644)
  • secret: global HMAC secret, used when no route defines its own
  • rate_limit: max requests per minute per route (default 30)
  • max_body_bytes: max payload size in bytes (default 1,048,576 = 1 MB)

Route Parameters

Each key under routes defines an endpoint at /webhooks/{route_name}:

  • secret: HMAC secret for this route (required — see security section)
  • events: list of accepted events (filtered by X-GitHub-Event, X-GitLab-Event, or event_type payload field)
  • prompt: prompt template with payload interpolation
  • skills: list of skills to inject into the agent''s context
  • deliver: delivery target (log, github_comment, telegram, discord, slack, etc.)
  • deliver_extra: delivery parameters (templates interpolated from payload)
  • deliver_only: if true, skip the agent — rendered template is delivered directly (push notification, zero LLM cost)

Trigger Events

GitHub Events

GitHub webhooks use the X-GitHub-Event header. Hermes automatically reads this header and compares it to the route''s events list. Common events:

  • push: a commit is pushed
  • pull_request: a PR is opened, updated, or closed
  • pull_request_review: a review is submitted
  • issues: an issue is created or modified
  • release: a new release is published
  • workflow_run: a GitHub Action completes

GitLab Events

The equivalent header is X-GitLab-Event. Events include Push Hook, Merge Request Hook, Note Hook, etc.

Custom Events

For any service that doesn''t use GitHub/GitLab headers, Hermes checks the event_type field in the JSON payload. This covers Sentry, Grafana, Supabase, Stripe, or any custom webhook.

CI/CD Pipeline Example

platforms:
  webhook:
    extra:
      routes:
        ci_pipeline:
          secret: "${WEBHOOK_CI_SECRET}"
          events:
            - pipeline.success
            - pipeline.failure
          prompt: >
            CI/CD pipeline completed for {project_name}:
            Status: {event_type}
            Commit: {commit_sha} by {author}
            Branch: {branch}
            Details: {__raw__}
          deliver: telegram
          deliver_extra:
            chat_id: "-1001234567890"

Handlers: Prompt Templates, Skills, and Context

Prompt Templates

Prompt templates support dot-notation interpolation for navigating nested JSON payloads:

{pull_request.title}          payload["pull_request"]["title"]
{pull_request.user.login}     payload["pull_request"]["user"]["login"]
{repository.full_name}        payload["repository"]["full_name"]

The special {__raw__} token dumps the entire payload as JSON (truncated to 4,000 characters), ideal when the agent needs full visibility into the data.

If no template is provided, Hermes generates a default prompt with the raw JSON payload.

Skill Injection

The skills parameter loads skill content directly into the prompt sent to the agent, bypassing slash commands. This gives the agent specific context for handling the webhook:

routes:
  github_pr_review:
    skills:
      - code-review
    prompt: "Review this PR: {pull_request.title}
{pull_request.body}"

The code-review skill is injected with its instructions and prompt template, guiding the agent''s analysis.

deliver_only Mode (Direct Push)

Setting deliver_only: true completely bypasses the agent. The rendered template becomes the message sent directly to the target platform. Use cases:

  • Monitoring alerts to Telegram/Discord (< 1 second latency)
  • Deployment notifications to a channel
  • Inter-agent pings (zero LLM cost)

Security

HMAC Signature Verification

Every route must have a secret configured. Hermes validates the HMAC signature before reading the payload (auth-before-body):

  • GitHub: validates X-Hub-Signature-256 (HMAC-SHA256)
  • GitLab: validates X-Gitlab-Token (direct comparison)
  • Generic: validates X-Webhook-Signature (HMAC-SHA256)

If a secret is configured but no signature header is found, the request is rejected (401 Unauthorized).

Testing Mode (INSECURE_NO_AUTH)

For local development, you can temporarily disable authentication:

routes:
  test_route:
    secret: "INSECURE_NO_AUTH"

⚠️ Never use in production. This mode skips all signature validation.

Rate Limiting

Hermes enforces per-route rate limiting (fixed 60-second window). Requests beyond the limit receive a 429 Too Many Responses. Default is 30 requests/minute per route.

Idempotency Cache

To prevent duplicates from provider retries, Hermes maintains a cache of delivery IDs (X-GitHub-Delivery or X-Request-ID) with a 1-hour TTL. Duplicate requests receive a 200 with status: "duplicate" instead of triggering a second agent run.

Payload Size Limits

The max_body_bytes parameter (default 1 MB) is checked before reading the body via Content-Length. Oversized payloads receive a 413 Payload Too Large.

Concrete Use Cases

1. Automated Pull Request Review

Configure Hermes to receive GitHub pull_request events and post a review comment directly on the PR:

routes:
  pr_review:
    secret: "${GITHUB_WEBHOOK_SECRET}"
    events: ["pull_request"]
    prompt: >
      Analyze this pull request carefully.
      Check code quality, potential risks, and suggest improvements.
      Repo: {repository.full_name}
      PR #{pull_request.number}: {pull_request.title}
      By: {pull_request.user.login}
      Description: {pull_request.body}
    skills: ["code-review"]
    deliver: github_comment
    deliver_extra:
      repo: "{repository.full_name}"
      pr_number: "{pull_request.number}"

2. Monitoring Alerts to Telegram

Receive Grafana or Sentry alerts and have the AI analyze incidents:

routes:
  grafana_alert:
    secret: "${GRAFANA_WEBHOOK_SECRET}"
    events: ["alerting"]
    prompt: >
      Monitoring alert received.
      Analyze the likely cause and propose an action plan.
      {__raw__}
    deliver: telegram
    deliver_extra:
      chat_id: "${ALERTS_CHAT_ID}"

3. Form Submissions

A website sends form submissions to Hermes, which processes and routes them:

routes:
  contact_form:
    secret: "${FORM_WEBHOOK_SECRET}"
    prompt: >
      New contact form submission:
      Name: {name}
      Email: {email}
      Subject: {subject}
      Message: {message}
      Categorize this request and suggest an appropriate response.
    deliver: slack
    deliver_extra:
      chat_id: "C12345SUPPORT"

4. Deployment Notifications (Direct Push)

For deployments, no AI needed — a raw notification suffices:

routes:
  deploy:
    secret: "${DEPLOY_WEBHOOK_SECRET}"
    events: ["deploy.success", "deploy.failure"]
    prompt: "🚀 Deployment {event_type} on {environment} (v{version})"
    deliver_only: true
    deliver: discord
    deliver_extra:
      chat_id: "${DEPLOYS_CHANNEL}"

GitHub Webhook Integration

GitHub Configuration

  1. Go to Settings → Webhooks → Add webhook
  2. Payload URL: http://your-server:8644/webhooks/github_pr_review
  3. Content type: application/json
  4. Secret: the same secret as in your config.yaml
  5. Events: select Pull requests, Push events, etc.

Secret Security

Store the secret in ~/.hermes/.env and reference it with ${VAR} in the config:

# ~/.hermes/.env
GITHUB_WEBHOOK_SECRET=whsec_abc123def456...
# config.yaml
routes:
  github_pr_review:
    secret: "${GITHUB_WEBHOOK_SECRET}"

Hermes automatically resolves environment variables in the configuration at startup.

ngrok Tunneling (Development)

For testing locally behind NAT:

ngrok http 8644
# → https://abc123.ngrok.io/webhooks/your_route

Use the ngrok URL as the Payload URL in GitHub settings.

Logs and Monitoring

Adapter Logs

Every webhook request is logged by the adapter:

[webhook] POST event=pull_request route=github_pr_review prompt_len=847 delivery=12345-abcde
[webhook] Posted comment on owner/repo#42
[webhook] Skipping duplicate delivery 12345-abcde
[webhook] Invalid signature for route grafana_alert

Health Check

The GET /health endpoint returns {"status": "ok", "platform": "webhook"}. Use it for your monitoring:

curl http://localhost:8644/health

Dynamic Routes

Hermes supports routes created dynamically by the agent itself. These routes are stored in ~/.hermes/webhook_subscriptions.json and are automatically reloaded on each request (mtime-gated check). Static routes (config.yaml) always take priority over dynamic routes.

Lifecycle Hooks

Hermes'' hook system (gateway/hooks.py) allows reacting to internal gateway events, independent of external webhooks:

  • gateway:startup — on startup
  • session:start / session:end — session lifecycle
  • agent:start / agent:step / agent:end — agent loop
  • command:* — wildcard for any slash command

You can combine hooks and webhooks: an agent:end hook could log statistics for every webhook response.

Troubleshooting

Port Already in Use

[webhook] Port 8644 already in use.

Change the port in config.yaml:

platforms:
  webhook:
    extra:
      port: 8645

Invalid Signature

[webhook] Invalid signature for route my_route
  • Verify the secret in config.yaml matches the provider''s configuration exactly
  • For GitHub, check the X-Hub-Signature-256 header (requires UTF-8 encoded secret)
  • For GitLab, check the X-Gitlab-Token header
  • For generic webhooks, use the X-Webhook-Signature header with the body''s HMAC-SHA256

Route Not Found (404)

Verify that the route name in the URL exactly matches the key in config.yaml. URLs are case-sensitive: /webhooks/GitHub_PR/webhooks/github_pr.

Delivery Failure

  • github_comment: install the gh CLI and authenticate (gh auth login)
  • Cross-platform: verify the target platform is enabled in config.yaml and connected
  • Missing chat_id: provide a chat_id in deliver_extra or configure a home_channel for the target platform

Payload Too Large (413)

Increase max_body_bytes if your webhooks send large payloads (e.g., very large PR diffs):

platforms:
  webhook:
    extra:
      max_body_bytes: 5242880  # 5 MB

Rate Limit (429)

If a service sends too many events, adjust the per-route rate_limit:

routes:
  busy_service:
    rate_limit: 100

Missing aiohttp Dependency

The webhook adapter requires aiohttp. If you get an import error:

pip install aiohttp

✅ Conclusion

Webhooks transform Hermes Agent into a true automation node capable of reacting to events from dozens of external services. The platform adapter architecture ensures that webhooks benefit from the same robust lifecycle as Telegram, Discord, or Slack — with additional advanced security mechanisms (HMAC, rate limiting, idempotency) and the flexibility of cross-platform routing.

To explore Hermes further:

Conclusion

Hermes Agent webhooks provide a bidirectional gateway between your AI agent and the external ecosystem. While cron jobs allow Hermes to take the initiative at regular intervals, webhooks let third-party services trigger actions in real time. Combined, cron jobs and webhooks cover the full automation spectrum: scheduled tasks on one side, reactive events on the other. Cross-platform routing flexibility, security mechanisms (HMAC, rate limiting), and asynchronous delivery support make webhooks an essential tool for integrating Hermes into any workflow.