A polling daemon that watches Fizzy for unread notifications and forwards them to an OpenClaw webhook.
OpenClaw's heartbeat is costly for simple notification checking, which is why this separate polling service exists. We can also added more instructions and references in the webhook payload for better result.
bundle installruby app.rb [--config FILE | --token TOKEN] [options]For a single agent, use the --token flag:
ruby app.rb --url https://app.fizzy.do --token abc123 --webhook-url http://localhost:18789 --webhook-token secretFor multiple agents, use a YAML config file:
ruby app.rb --config config.ymlCreate a config.yml (see config.example.yml):
url: https://app.fizzy.do
webhook_url: http://localhost:18789
webhook_token: your-webhook-token
polling: 10
agents:
- name: optimus
token: optimus-fizzy-token
- name: wheeljack
token: wheeljack-fizzy-token
- name: prowl
token: prowl-fizzy-tokenEach agent's notifications are polled independently. The webhook payload includes the agent name so OpenClaw can route to the correct session:
{
"agent": "optimus",
"message": "You have a new notification...",
"mode": "now",
"deliver": false
}| Flag | Required | Description | Default |
|---|---|---|---|
--url URL |
Yes* | Fizzy base URL (e.g. https://app.fizzy.do) |
— |
--token TOKEN |
Yes* | Fizzy personal access token (single agent mode) | — |
--config FILE |
Yes* | YAML config file for multi-agent mode | — |
--webhook-url URL |
No | OpenClaw webhook base URL | — |
--webhook-token TOKEN |
No | OpenClaw webhook bearer token | — |
--polling SECONDS |
No | Polling interval in seconds | 10 |
--dry-run |
No | Print webhook payload without sending | — |
--verbose |
No | Print full request/response headers and body (redacts Authorization) | — |
*Either --token or --config is required. When using --config, the url can be specified in the config file.
Both --webhook-url and --webhook-token are required unless --dry-run is used.
Dry run (single agent, no webhook):
ruby app.rb --url https://app.fizzy.do --token abc123 --dry-runSingle agent with webhook forwarding:
ruby app.rb \
--url https://app.fizzy.do \
--token abc123 \
--webhook-url http://localhost:18789 \
--webhook-token my-secretMulti-agent with config file:
ruby app.rb --config config.ymlMulti-agent dry run:
ruby app.rb --config config.yml --dry-run --verboseRun as background daemon:
nohup ruby app.rb --config config.yml > fizzy-pop.log 2>&1 &docker build -t fizzy-pop .Single agent mode:
docker run --rm fizzy-pop \
--url https://app.fizzy.do \
--token abc123 \
--webhook-url http://host.docker.internal:18789 \
--webhook-token my-secretMulti-agent mode (mount config file):
docker run --rm \
-v $(pwd)/config.yml:/app/config.yml:ro \
fizzy-pop \
--config /app/config.ymlIf the OpenClaw gateway is running on the host machine, add --add-host so the container can reach it:
docker run --rm \
--add-host host.docker.internal:host-gateway \
-v $(pwd)/config.yml:/app/config.yml:ro \
fizzy-pop \
--config /app/config.yml \
--verboseFizzy Pop includes a Kamal configuration for deploying to a remote server.
Create a .env file in the project root:
HOSTS=your-server-ip
URL=https://app.fizzy.do
TOKEN=your-fizzy-token
WEBHOOK_URL=http://host.docker.internal:18789
WEBHOOK_TOKEN=your-webhook-token
For multi-agent mode, mount your config.yml instead of using TOKEN.
The bin/kamal binstub automatically loads .env before running Kamal. Environment variables already set in your shell take precedence.
bin/kamal setup # First-time server setup and deploy
bin/kamal deploy # Subsequent deploysbin/kamal app logs # Tail container logs
bin/kamal app details # Show running container info
bin/kamal app stop # Stop the service
bin/kamal app start # Start the serviceThe deploy config (config/deploy.yml) automatically passes --add-host host.docker.internal:host-gateway so the container can reach services on the host machine.
Enable webhooks and configure the gateway in your OpenClaw config:
{
gateway: {
port: 18789,
mode: "local",
bind: "lan"
},
hooks: {
enabled: true,
token: "your-webhook-token"
}
}Setting bind to "lan" is required when Fizzy Pop runs inside a Docker container, because the default "loopback" mode only listens on 127.0.0.1 which is unreachable from within a container. The "lan" mode binds to 0.0.0.0 so the container can connect via host.docker.internal. Note that "lan" mode requires authentication to be configured — the gateway will refuse to start without it.
If you run Fizzy Pop directly on the same host (not in Docker), you can use bind: "loopback" instead.
See the OpenClaw security docs for all bind modes (loopback, lan, tailnet, auto).
The hooks.token value is what you pass as --webhook-token. The --webhook-url should point to your gateway (e.g. http://localhost:18789).
Fizzy Pop posts to POST /hooks/agent with Authorization: Bearer <token>.
When using multi-agent mode, the webhook payload includes an agent field:
{
"agent": "optimus",
"message": "...",
"mode": "now",
"deliver": false
}Configure OpenClaw to route based on the agent name. See the OpenClaw webhook docs for more details.
- Authenticates with Fizzy using personal access token(s)
- Polls for unread notifications every N seconds (for each agent in multi-agent mode)
- For each unread notification with a creator (comments/mentions):
- Marks it as read in Fizzy
- Forwards the notification to the OpenClaw webhook (
POST /hooks/agent) - Includes agent identifier in payload (multi-agent mode)
