A WebSocket-to-HTTP event bridge for Webex, written in Go using the webex-go-sdk.
Hookbuster connects to the Webex Mercury WebSocket service and forwards real-time events as HTTP POST requests to a local target application — eliminating the need for public webhook URLs during development.
- Real-time event forwarding via Webex Mercury WebSocket
- Supported resources: rooms, messages, memberships, attachmentActions
- Interactive CLI for guided setup
- Environment variable mode for automated / container deployments
- Multi-pipeline mode via YAML config file — multiple tokens and/or fan-out to multiple webhooks
- Forwarding modes:
fanout(send to all targets) androundrobin(load-balanced with health checks and retry) - Firehose mode subscribes to all resources and all events
- Graceful shutdown on SIGINT / SIGTERM
- End-to-end decryption of message content via the SDK's KMS integration
| Resource | Events |
|---|---|
| rooms | created, updated |
| messages | created, deleted |
| memberships | created, updated, deleted |
| attachmentActions | created |
- Go 1.26+
- A Webex access token (get one at https://developer.webex.com)
go build -o hookbuster ../hookbusterYou will be prompted for:
- Webex access token
- Forwarding target (e.g.
localhost) - Forwarding port (e.g.
8080) - Resource selection
- Event selection
TOKEN=<your-webex-token> PORT=8080 TARGET=localhost ./hookbusterWhen TOKEN and PORT are set, hookbuster automatically subscribes to all resources with all events (firehose mode).
| Variable | Required | Default | Description |
|---|---|---|---|
TOKEN |
Yes | — | Webex access token |
PORT |
Yes | — | Target forwarding port |
TARGET |
No | localhost |
Target hostname or IP address |
Create a hookbuster.yml config file (see hookbuster.yml.example):
pipelines:
- name: "bot-account"
token_env: "WEBEX_TOKEN_BOT" # env var name (token never in file)
mode: "fanout" # must be explicit — default is roundrobin
resources: ["messages", "rooms"]
events: "all"
targets:
- url: "http://localhost:8080"
- name: "main-account"
token_env: "WEBEX_TOKEN_MAIN"
mode: "fanout" # must be explicit — default is roundrobin
resources: ["messages", "rooms", "memberships", "attachmentActions"]
events: "all"
targets: # fan-out to multiple targets
- url: "http://localhost:3000"
- url: "http://localhost:4000"
- name: "load-balanced"
token_env: "WEBEX_TOKEN_LB"
mode: "roundrobin" # default — can be omitted
resources: ["messages"]
events: "all"
targets:
- url: "http://localhost:5001"
- url: "http://localhost:5002"
- url: "http://localhost:5003"Then run with the -c flag or HOOKBUSTER_CONFIG environment variable:
# Via flag
./hookbuster -c hookbuster.yml
# Via environment variable
HOOKBUSTER_CONFIG=hookbuster.yml ./hookbusterKey features:
- Multi-token: Each pipeline connects with its own Webex token
- Round-robin (
mode: roundrobin): Distribute events across targets with automatic retry, health checks (mark unhealthy after 3 consecutive failures), and background recovery (probe every 10 s) (default) - Fan-out (
mode: fanout): Send every event to all targets simultaneously - Security: Tokens are referenced by env var name — never stored in the config file
- Firehose shorthand: Omit
resourcesto subscribe to all resources
| Config Field | Required | Default | Description |
|---|---|---|---|
name |
No | — | Pipeline name (used in log output) |
token_env |
Yes | — | Env var name holding the Webex token |
mode |
No | roundrobin |
Forwarding mode: fanout or roundrobin |
resources |
No | all | Resources to subscribe to |
events |
No | all |
Event filter (all or specific event) |
targets |
Yes | — | One or more target URLs |
Build and run from the parent directory (which contains both webex-go-sdk/ and webex-go-hookbuster/):
docker build -f webex-go-hookbuster/Dockerfile -t hookbuster .
docker run -e TOKEN=<your-token> -e PORT=8080 -e TARGET=host.docker.internal hookbusterflowchart TD
A["Webex Cloud\n(Mercury WebSocket)"] --> B["Conversation Client\nParses activities, decrypts messages,\ndispatches by verb"]
B --> C["Listener\nMaps verbs to resource/event pairs,\nfilters by subscription"]
C --> D["Forwarder\nHTTP POST to target:port"]
D --> E["Your Application\n(localhost:port)"]
style A fill:#4a90d9,color:#fff
style B fill:#5ba55b,color:#fff
style C fill:#e8a838,color:#fff
style D fill:#d94a4a,color:#fff
style E fill:#7b68ee,color:#fff
flowchart TD
subgraph bot["Pipeline: bot — fanout ($BOT_TOKEN)"]
B1["Conversation Client"] --> B2["Listener"]
B2 --> B3["localhost:8080"]
end
subgraph main["Pipeline: main — fanout ($MAIN_TOKEN)"]
M1["Conversation Client"] --> M2["Listener"]
M2 --> M3["localhost:3000"]
M2 --> M4["localhost:4000"]
end
subgraph lb["Pipeline: lb — roundrobin ($LB_TOKEN)"]
L1["Conversation Client"] --> L2["Listener"]
L2 --> L3["Balancer\n(round-robin + health checks)"]
L3 --> L4["localhost:5001"]
L3 --> L5["localhost:5002"]
L3 --> L6["localhost:5003"]
end
style bot fill:#e8f4f8,stroke:#4a90d9
style main fill:#f0f8e8,stroke:#5ba55b
style lb fill:#fff3e0,stroke:#e8a838
style B1 fill:#5ba55b,color:#fff
style B2 fill:#e8a838,color:#fff
style B3 fill:#7b68ee,color:#fff
style M1 fill:#5ba55b,color:#fff
style M2 fill:#e8a838,color:#fff
style M3 fill:#7b68ee,color:#fff
style M4 fill:#7b68ee,color:#fff
style L1 fill:#5ba55b,color:#fff
style L2 fill:#e8a838,color:#fff
style L3 fill:#d94a4a,color:#fff
style L4 fill:#7b68ee,color:#fff
style L5 fill:#7b68ee,color:#fff
style L6 fill:#7b68ee,color:#fff
MPL-2.0