Webhooks & Events
godotz.ai emits structured events at every meaningful state transition: task lifecycle, agent status, budget alerts, and fleet health changes. Events are delivered via ntfy (push notifications) and optionally forwarded to HTTP webhooks.
ntfy Integration
godotz.ai uses ntfy as its primary notification transport. ntfy is self-hostable and has native mobile/desktop clients.
Configuration
# ~/.config/omp/config.yml (notifications section)
notifications:
ntfy:
enabled: true
server: https://ntfy.sh # Or your self-hosted instance
topic: omp-fleet-<your-token> # Unique topic per fleet
priority: default # min | low | default | high | urgent
token: "" # ntfy access token (if server requires auth)
Subscribe to events
# Terminal (curl stream)
curl -s https://ntfy.sh/omp-fleet-abc123/json
# ntfy CLI
ntfy subscribe omp-fleet-abc123
Fleet Events
Event Taxonomy
| Event | Source | Severity |
|---|---|---|
agent.started | godotz.ai agent | info |
agent.stopped | godotz.ai agent | info |
agent.crashed | godotz.ai agent | high |
task.created | beads | info |
task.done | beads | info |
task.blocked | beads | warn |
epic.closed | beads | info |
budget.alert | LiteLLM | warn |
budget.exceeded | LiteLLM | urgent |
model.degraded | LiteLLM health | warn |
model.unavailable | LiteLLM health | urgent |
node.offline | fleet monitor | urgent |
swarm.complete | swarm runtime | info |
swarm.timeout | swarm runtime | warn |
Notification Payload Format
All godotz.ai events follow a common JSON envelope.
{
"event": "task.done",
"version": "1",
"timestamp": "2026-06-07T14:22:00Z",
"fleet": "home-lab",
"node": "pi-04",
"agent": "executor-01",
"data": {
"task_id": 42,
"task_title": "Update NixOS flake inputs on pi-04",
"epic_id": 7,
"duration_seconds": 38
},
"meta": {
"swarm": "fleet-upgrade-swarm",
"build": "v15.10.1"
}
}
ntfy delivery maps the envelope to ntfy headers:
| JSON field | ntfy header |
|---|---|
event | X-Title |
data.task_title or summary | notification body |
| severity | X-Priority |
| node + agent | X-Tags |
omp-notify Script
omp-notify is the canonical way to emit events from hooks or custom scripts. It reads ~/.config/omp/config.yml for ntfy settings.
Usage
omp-notify [--event EVENT] [--data JSON] [--priority PRIORITY] [--title TEXT]
Examples:
# Signal task complete
omp-notify \
--event task.done \
--title "Task #42 done: flake inputs updated" \
--data '{"task_id": 42, "node": "pi-04"}'
# Budget alert (urgent)
omp-notify \
--event budget.alert \
--title "Budget at 80% — executor-pool" \
--priority high
# Swarm complete
omp-notify \
--event swarm.complete \
--title "fleet-upgrade-swarm finished in 47m" \
--data '{"swarm": "fleet-upgrade-swarm", "duration": "47m", "cost_usd": 3.21}'
Exit codes
| Code | Meaning |
|---|---|
| 0 | Notification delivered |
| 1 | Configuration error |
| 2 | ntfy server unreachable |
| 3 | Authentication failed |
HTTP Webhooks
In addition to ntfy, godotz.ai can POST events to any HTTP endpoint.
# ~/.config/omp/config.yml
notifications:
webhooks:
- url: https://hooks.example.com/omp
events: ["agent.crashed", "budget.exceeded", "node.offline"]
secret: "whsec_your_signing_secret"
timeout_seconds: 5
retry: 3
The request body is the standard JSON envelope. A X-OMP-Signature header contains HMAC-SHA256(secret, body) for verification.
Verification example (Python):
import hmac, hashlib
def verify_omp_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Custom Event Hooks
Register shell hooks that fire on any event:
# Register a hook for agent crashes
omp hooks add agent.crashed /opt/omp/hooks/restart-agent.sh
Hook scripts receive the full JSON envelope as $OMP_EVENT:
#!/usr/bin/env bash
# /opt/omp/hooks/restart-agent.sh
AGENT=$(echo "$OMP_EVENT" | jq -r '.agent')
NODE=$(echo "$OMP_EVENT" | jq -r '.node')
echo "Restarting $AGENT on $NODE..."
ssh "$NODE" "systemctl restart omp-agent@$AGENT"
Testing Events
Emit a test event without triggering real side-effects:
omp-notify --event test --title "OMP notification test" --dry-run
# DRY RUN: would POST to https://ntfy.sh/omp-fleet-abc123
# Payload: {"event":"test","title":"OMP notification test",...}
Related
- beads CLI — task events source
- Fleet Setup — configuring ntfy per node
- godotz.ai Agent Config — notification config block