$npx -y skills add adityahimaone/hermes-agent-rtk-caveman --skill webhook-subscriptionsCreate and manage webhook subscriptions for event-driven agent activation. Use when the user wants external services to trigger agent runs automatically.
| 1 | # Webhook Subscriptions |
| 2 | |
| 3 | Create dynamic webhook subscriptions so external services (GitHub, GitLab, Stripe, CI/CD, IoT sensors, monitoring tools) can trigger Hermes agent runs by POSTing events to a URL. |
| 4 | |
| 5 | ## Setup (Required First) |
| 6 | |
| 7 | The webhook platform must be enabled before subscriptions can be created. Check with: |
| 8 | ```bash |
| 9 | hermes webhook list |
| 10 | ``` |
| 11 | |
| 12 | If it says "Webhook platform is not enabled", set it up: |
| 13 | |
| 14 | ### Option 1: Setup wizard |
| 15 | ```bash |
| 16 | hermes gateway setup |
| 17 | ``` |
| 18 | Follow the prompts to enable webhooks, set the port, and set a global HMAC secret. |
| 19 | |
| 20 | ### Option 2: Manual config |
| 21 | Add to `~/.hermes/config.yaml`: |
| 22 | ```yaml |
| 23 | platforms: |
| 24 | webhook: |
| 25 | enabled: true |
| 26 | extra: |
| 27 | host: "0.0.0.0" |
| 28 | port: 8644 |
| 29 | secret: "generate-a-strong-secret-here" |
| 30 | ``` |
| 31 | |
| 32 | ### Option 3: Environment variables |
| 33 | Add to `~/.hermes/.env`: |
| 34 | ```bash |
| 35 | WEBHOOK_ENABLED=true |
| 36 | WEBHOOK_PORT=8644 |
| 37 | WEBHOOK_SECRET=generate-a-strong-secret-here |
| 38 | ``` |
| 39 | |
| 40 | After configuration, start (or restart) the gateway: |
| 41 | ```bash |
| 42 | hermes gateway run |
| 43 | # Or if using systemd: |
| 44 | systemctl --user restart hermes-gateway |
| 45 | ``` |
| 46 | |
| 47 | Verify it's running: |
| 48 | ```bash |
| 49 | curl http://localhost:8644/health |
| 50 | ``` |
| 51 | |
| 52 | ## Commands |
| 53 | |
| 54 | All management is via the `hermes webhook` CLI command: |
| 55 | |
| 56 | ### Create a subscription |
| 57 | ```bash |
| 58 | hermes webhook subscribe <name> \ |
| 59 | --prompt "Prompt template with {payload.fields}" \ |
| 60 | --events "event1,event2" \ |
| 61 | --description "What this does" \ |
| 62 | --skills "skill1,skill2" \ |
| 63 | --deliver telegram \ |
| 64 | --deliver-chat-id "12345" \ |
| 65 | --secret "optional-custom-secret" |
| 66 | ``` |
| 67 | |
| 68 | Returns the webhook URL and HMAC secret. The user configures their service to POST to that URL. |
| 69 | |
| 70 | ### List subscriptions |
| 71 | ```bash |
| 72 | hermes webhook list |
| 73 | ``` |
| 74 | |
| 75 | ### Remove a subscription |
| 76 | ```bash |
| 77 | hermes webhook remove <name> |
| 78 | ``` |
| 79 | |
| 80 | ### Test a subscription |
| 81 | ```bash |
| 82 | hermes webhook test <name> |
| 83 | hermes webhook test <name> --payload '{"key": "value"}' |
| 84 | ``` |
| 85 | |
| 86 | ## Prompt Templates |
| 87 | |
| 88 | Prompts support `{dot.notation}` for accessing nested payload fields: |
| 89 | |
| 90 | - `{issue.title}` — GitHub issue title |
| 91 | - `{pull_request.user.login}` — PR author |
| 92 | - `{data.object.amount}` — Stripe payment amount |
| 93 | - `{sensor.temperature}` — IoT sensor reading |
| 94 | |
| 95 | If no prompt is specified, the full JSON payload is dumped into the agent prompt. |
| 96 | |
| 97 | ## Common Patterns |
| 98 | |
| 99 | ### GitHub: new issues |
| 100 | ```bash |
| 101 | hermes webhook subscribe github-issues \ |
| 102 | --events "issues" \ |
| 103 | --prompt "New GitHub issue #{issue.number}: {issue.title}\n\nAction: {action}\nAuthor: {issue.user.login}\nBody:\n{issue.body}\n\nPlease triage this issue." \ |
| 104 | --deliver telegram \ |
| 105 | --deliver-chat-id "-100123456789" |
| 106 | ``` |
| 107 | |
| 108 | Then in GitHub repo Settings → Webhooks → Add webhook: |
| 109 | - Payload URL: the returned webhook_url |
| 110 | - Content type: application/json |
| 111 | - Secret: the returned secret |
| 112 | - Events: "Issues" |
| 113 | |
| 114 | ### GitHub: PR reviews |
| 115 | ```bash |
| 116 | hermes webhook subscribe github-prs \ |
| 117 | --events "pull_request" \ |
| 118 | --prompt "PR #{pull_request.number} {action}: {pull_request.title}\nBy: {pull_request.user.login}\nBranch: {pull_request.head.ref}\n\n{pull_request.body}" \ |
| 119 | --skills "github-code-review" \ |
| 120 | --deliver github_comment |
| 121 | ``` |
| 122 | |
| 123 | ### Stripe: payment events |
| 124 | ```bash |
| 125 | hermes webhook subscribe stripe-payments \ |
| 126 | --events "payment_intent.succeeded,payment_intent.payment_failed" \ |
| 127 | --prompt "Payment {data.object.status}: {data.object.amount} cents from {data.object.receipt_email}" \ |
| 128 | --deliver telegram \ |
| 129 | --deliver-chat-id "-100123456789" |
| 130 | ``` |
| 131 | |
| 132 | ### CI/CD: build notifications |
| 133 | ```bash |
| 134 | hermes webhook subscribe ci-builds \ |
| 135 | --events "pipeline" \ |
| 136 | --prompt "Build {object_attributes.status} on {project.name} branch {object_attributes.ref}\nCommit: {commit.message}" \ |
| 137 | --deliver discord \ |
| 138 | --deliver-chat-id "1234567890" |
| 139 | ``` |
| 140 | |
| 141 | ### Generic monitoring alert |
| 142 | ```bash |
| 143 | hermes webhook subscribe alerts \ |
| 144 | --prompt "Alert: {alert.name}\nSeverity: {alert.severity}\nMessage: {alert.message}\n\nPlease investigate and suggest remediation." \ |
| 145 | --deliver origin |
| 146 | ``` |
| 147 | |
| 148 | ## Security |
| 149 | |
| 150 | - Each subscription gets an auto-generated HMAC-SHA256 secret (or provide your own with `--secret`) |
| 151 | - The webhook adapter validates signatures on every incoming POST |
| 152 | - Static routes from config.yaml cannot be overwritten by dynamic subscriptions |
| 153 | - Subscriptions persist to `~/.hermes/webhook_subscriptions.json` |
| 154 | |
| 155 | ## How It Works |
| 156 | |
| 157 | 1. `hermes webhook subscribe` writes to `~/.hermes/webhook_subscriptions.json` |
| 158 | 2. The webhook adapter hot-reloads this file on each incoming request (mtime-gated, negligible overhead) |
| 159 | 3. When a POST arrives matching a route, the adapter formats the prompt and triggers an agent run |
| 160 | 4. The agent's response is delivered to the configured target (Telegram, Discord, GitHub comment, etc.) |
| 161 | |
| 162 | ## Troubleshooting |
| 163 | |
| 164 | If webhooks aren't working: |
| 165 | |
| 166 | 1. **Is the gateway running?** Check with `systemct |