Webhooks: Trigger Hermes From GitHub, GitLab and More
The webhook adapter turns Hermes into an event-driven agent: it runs an HTTP server that accepts POST requests from external services like GitHub, GitLab, JIRA, and Stripe, validates HMAC signatures, transforms the payload into an agent prompt, and routes the response back to the source or to another configured platform. The agent can post comments on pull requests, send messages to Telegram or Discord, or log the result — all automatically whenever an event fires.
Quick Start
Enable the adapter in two ways. Run hermes gateway setup and follow the prompts to turn on webhooks, set the port, and set a global HMAC secret. Or set environment variables in ~/.hermes/.env:
WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644
WEBHOOK_SECRET=your-global-secret
Once the gateway is running, verify it with curl http://localhost:8644/health, which should return {"status": "ok", "platform": "webhook"}. Then point your service at http://your-server:8644/webhooks/<route-name>.
Configuring Routes
Routes define how different webhook sources are handled. Each is a named entry under platforms.webhook.extra.routes in config.yaml, with properties like events (which event types to accept, e.g. ["pull_request"]), secret (HMAC secret for validation), prompt (a template with dot-notation payload access like {pull_request.title}), skills (skills to load for the agent run), and deliver (where the response goes). A full GitHub PR review route might look like:
platforms:
webhook:
extra:
routes:
github-pr:
events: ["pull_request"]
secret: "github-webhook-secret"
prompt: |
Review this pull request:
Repository: {repository.full_name}
PR #{number}: {pull_request.title}
skills: ["github-code-review"]
deliver: "github_comment"
To set this up end to end, create the webhook in GitHub pointing at the route URL, add the route config, ensure gh is authenticated with gh auth login, and open a pull request — Hermes fires, processes the event, and posts a review comment. GitLab works similarly but authenticates differently, sending the secret as a plain X-Gitlab-Token header instead of an HMAC.
Filters and Templates
When a provider sends a broad event stream but only some payloads should wake the agent, use payload filters — declarative conditions evaluated after auth and event filtering but before agent dispatch. Supported operators include equals, not_equals, contains, in, in_file, regex, and all/any/not groups, using dot-notation field paths. When declarative filters aren't enough, a script under ~/.hermes/scripts/ can filter or transform the payload as JSON on stdin.
Prompts use dot-notation to access nested payload fields — {repository.full_name} resolves to payload["repository"]["full_name"]. The {__raw__} token dumps the entire payload as indented JSON (truncated at 4000 characters), ideal for monitoring alerts or generic webhooks. Missing keys are left literally rather than erroring, and nested structures are JSON-serialized with a 2000-character cap.
Delivery Options
The deliver field controls where the agent's response goes:
log— logs to the gateway output (the default, useful for testing)github_comment— posts a PR/issue comment via theghCLItelegram,discord,slack,signal,sms,whatsapp,matrix,mattermost— route to a messaging platformemail,dingtalk,feishu,wecom, and more
Cross-platform targets must be enabled and connected in the gateway; when no chat_id is provided, the response goes to the platform's configured home channel.
Direct Delivery Mode
By default, every webhook POST triggers an agent run — which costs LLM tokens on every event. For plain notifications that need no reasoning, set deliver_only: true on a route. The rendered prompt template becomes the literal message body, and the adapter dispatches it directly. The benefits are real: zero LLM tokens (the agent is never invoked), sub-second delivery (a single adapter call), the same security (HMAC auth, rate limits, idempotency, body-size limits), and synchronous responses so upstream services can retry intelligently — the POST returns 200 OK once delivery succeeds, or 502 if the target rejects it.
This is ideal for external pushes (Supabase or Firebase firing on a database change → notify a user in Telegram), monitoring alerts (Datadog or Grafana → a Discord channel), inter-agent pings, and background job completion. Webhook agent runs default to a deliberately constrained toolset — web_search, web_extract, vision_analyze, clarify — because public payloads can carry untrusted third-party content. For trusted routes you can grant a wider toolset, but only via manual config edits, so an agent can never self-grant elevated tools.
Key Takeaways
- The webhook adapter runs an HTTP server that validates HMAC signatures and turns events into agent prompts.
- Point GitHub, GitLab, Stripe, or any custom service at
http://your-server:8644/webhooks/<route>. - Routes support event filtering, payload filters, script transforms, and dot-notation prompt templates.
- Responses route to GitHub comments, messaging platforms, email, or the gateway log.
deliver_only: truegives zero-token, sub-second notifications with the same security as agent mode.