Webhooks
Receive events from external services (GitHub, GitLab, Stripe, etc.) and react with AI agents, notifications, or tool executions.
How It Works
Section titled “How It Works”External Service ──POST──▶ /api/webhooks/:path ──▶ Hook Manager ──▶ Action (HMAC verified) (match by path)- An external service sends an HTTP POST to
https://<your-domain>/api/webhooks/<path> - The webhook endpoint verifies the HMAC-SHA256 signature against the hook’s
webhookSecret - All hooks with matching
webhookPathare triggered - Each hook’s action runs — spawn an agent, send a notification, call another webhook, etc.
- If
notifyOwner: trueis set on aspawn_agentaction, the agent’s output is automatically sent to the owner’s linked channels (Telegram, Slack, etc.)
Prerequisites
Section titled “Prerequisites”1. Public URL
Section titled “1. Public URL”The assistant backend must be reachable from the internet. Options:
-
Cloudflare Tunnel (recommended) — add an ingress rule in your tunnel config:
cloudflare/config.yml ingress:- hostname: app.your-domain.comservice: http://localhost:3005Then add a CNAME DNS record:
app→<tunnel-id>.cfargotunnel.com(proxied). -
ngrok — quick dev setup:
ngrok http 3005 -
Reverse proxy — nginx/caddy with TLS termination pointing to port 3005
2. Channel Bindings (for notifications)
Section titled “2. Channel Bindings (for notifications)”If you want Octipus to notify you about webhook events:
- Link your Telegram/Slack account in Settings → Channels (or via the
/linkcommand) - Verify the binding — the channel must show as “verified” in your user profile
- Set
notifyOwner: truein the hook’s action config
3. Webhook Secret
Section titled “3. Webhook Secret”Generate a random secret for HMAC signature verification:
openssl rand -hex 20Both sides (Octipus hook and the external service) must share this secret. Never skip this — hooks without a webhookSecret are rejected with 401.
Setup Guide: GitHub
Section titled “Setup Guide: GitHub”-
Create the Hook
Via API:
Terminal window curl -X POST http://localhost:3005/api/hooks \-H "Authorization: Bearer <token>" \-H "Content-Type: application/json" \-d '{"name": "GitHub Notifications","trigger": "webhook","triggerConfig": {"webhookPath": "github","webhookSecret": "<your-secret>"},"action": "spawn_agent","actionConfig": {"agentPrompt": "A GitHub webhook event was received. Analyze the payload and produce a concise summary.","agentTopic": "communication","orchestrated": false,"notifyOwner": true}}'Or use the Hooks page in the web UI.
-
Configure GitHub
Terminal window gh api repos/OWNER/REPO/hooks -X POST --input - <<'EOF'{"name": "web","active": true,"events": ["pull_request", "issues", "push"],"config": {"url": "https://app.your-domain.com/api/webhooks/github","content_type": "json","secret": "<your-secret>","insecure_ssl": "0"}}EOF- Go to repo Settings → Webhooks → Add webhook
- Payload URL:
https://app.your-domain.com/api/webhooks/github - Content type:
application/json - Secret: the same secret from Step 1
- Events: select “Pull requests”, “Issues”, “Pushes” (or “Send me everything”)
-
Verify
GitHub sends a
pingevent immediately after creating the webhook. Check the Hooks → Execution Log in the web UI, or:Terminal window # GitHub delivery historygh api repos/OWNER/REPO/hooks/<hook-id>/deliveries \--jq '.[0] | "\(.event) → \(.status_code)"'# Octipus execution logcurl http://localhost:3005/api/hooks/<hook-id>/executions \-H "Authorization: Bearer <token>" | jq '.executions[0]'
Setup Guide: GitLab
Section titled “Setup Guide: GitLab”-
Create the Hook
Same as GitHub, but use a different
webhookPath:{"triggerConfig": {"webhookPath": "gitlab","webhookSecret": "<your-secret>"}} -
Configure GitLab
- Go to project Settings → Webhooks
- URL:
https://app.your-domain.com/api/webhooks/gitlab - Secret token: your webhook secret
- Triggers: Push events, Merge request events, etc.
Setup Guide: Generic Webhook
Section titled “Setup Guide: Generic Webhook”Any service that sends JSON POST requests with HMAC-SHA256 signatures works out of the box.
Signature Format
Section titled “Signature Format”The sender must include an X-Hub-Signature-256 header:
X-Hub-Signature-256: sha256=<hex-encoded HMAC-SHA256 of raw request body>Manual Test
Section titled “Manual Test”PAYLOAD='{"event": "deploy", "status": "success"}'SECRET="your-webhook-secret"SIG="sha256=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')"
curl -X POST https://app.your-domain.com/api/webhooks/deploy \ -H "Content-Type: application/json" \ -H "X-Hub-Signature-256: $SIG" \ -d "$PAYLOAD"Action Types
Section titled “Action Types”spawn_agent (AI analysis + notification)
Section titled “spawn_agent (AI analysis + notification)”Best for events that need interpretation before notification.
{ "action": "spawn_agent", "actionConfig": { "agentPrompt": "Analyze this deployment event and summarize.", "agentTopic": "devops", "orchestrated": false, "notifyOwner": true }}orchestrated: false— spawns a single agent directly (faster)orchestrated: true— routes through the orchestrator for role classification and full tool accessnotifyOwner: true— sends the agent’s output to all verified channels when done
notify (direct notification)
Section titled “notify (direct notification)”Best for simple alerts that don’t need AI processing.
{ "action": "notify", "actionConfig": { "notifyOwner": true, "notifyMessage": "Webhook received: {{webhook.body.event}}" }}webhook (forward/chain)
Section titled “webhook (forward/chain)”Forward events to other services.
{ "action": "webhook", "actionConfig": { "webhookUrl": "https://other-service.com/api/events", "webhookMethod": "POST" }}execute_tool (run a tool directly)
Section titled “execute_tool (run a tool directly)”Automated responses like running a git pull or shell command.
{ "action": "execute_tool", "actionConfig": { "toolId": "shell", "toolAction": "execute", "toolParams": { "command": "cd /app && git pull origin main" } }}Webhook Context in Agent Prompts
Section titled “Webhook Context in Agent Prompts”When a webhook triggers a spawn_agent action, the payload is automatically appended to the agent prompt:
<your prompt text>
--- Webhook Payload ---{ "action": "opened", "pull_request": { ... } }Event type: pull_requestThe Event type line is extracted from X-GitHub-Event or X-GitLab-Event headers when present.
You can also use template variables in prompts — see Hooks & Automation for the full list.
Security
Section titled “Security”- HMAC-SHA256 required — every webhook hook must have a
webhookSecret. Hooks without one reject requests with 401. - Signature verification — uses
X-Hub-Signature-256header with timing-safe comparison. - Public endpoint —
/api/webhooks/*is excluded from bearer token auth (it uses HMAC instead). - Per-hook secrets — each hook can have a different secret for different services.
Multiple Webhook Paths
Section titled “Multiple Webhook Paths”Create multiple hooks with different paths for different services:
/api/webhooks/github → GitHub events/api/webhooks/gitlab → GitLab events/api/webhooks/stripe → Payment events/api/webhooks/deploy → CI/CD notifications/api/webhooks/monitoring → Alerting systemsEach path can have its own secret and action configuration.
Troubleshooting
Section titled “Troubleshooting”| Problem | Check |
|---|---|
| 401 on webhook | Verify the secret matches on both sides. Check X-Hub-Signature-256 header is present. |
| 404 on webhook | Ensure the URL path matches webhookPath in trigger config. |
| Hook not firing | Check hook is enabled. Verify webhookPath matches. Look at execution log. |
| Agent runs but no notification | Ensure notifyOwner: true in action config. Verify channel bindings are verified. |
| Agent doesn’t see payload | The payload is auto-appended. Check execution log → trigger context. |
| 502 from tunnel | Verify backend is running on port 3005. Check tunnel ingress rule. |