13 KiB
WhatsHooked
A service that connects to WhatsApp via the whatsmeow API and forwards messages to registered webhooks. Enables two-way communication by allowing webhooks to respond with messages to be sent through WhatsApp.
TODO LIST - Things I still need to do
Phase 1 Features
- Multi-Account Support: Connect to multiple WhatsApp accounts simultaneously
- Webhook Integration: Register multiple webhooks to receive WhatsApp messages
- Two-Way Communication: Webhooks can respond with messages to send back to WhatsApp
- Instance/Config Level Hooks: Global hooks that receive all messages from all accounts
- Media Support: Send and receive images, videos, and documents
- CLI Management: Command-line tool for managing accounts and hooks
- Structured Logging: JSON-based logging with configurable log levels
- Authentication: HTTP Basic Auth and API key authentication for server endpoints
Architecture
The project uses an event-driven architecture with the following packages:
- internal/config: Configuration management and persistence
- internal/logging: Structured logging using Go's slog package
- internal/events: Event bus for publish/subscribe messaging between components
- internal/whatsapp: WhatsApp client management using whatsmeow
- internal/hooks: Webhook management and message forwarding
- cmd/server: Main server application
- cmd/cli: Command-line interface for management
Event-Driven Architecture
The system uses a central event bus to decouple components:
-
WhatsApp Events → Event Bus → Hook Manager
- Connection/disconnection events
- Message received events
- Message sent/failed events
-
Hook Events → Event Bus → WhatsApp Manager
- Hook triggered events
- Hook success/failure events
- Webhook responses trigger message sends
This architecture enables:
- Loose coupling between WhatsApp and webhooks
- Easy addition of new event subscribers
- Centralized event logging and monitoring
- Two-way communication through event responses
- Context propagation for cancellation and timeout handling
- Proper request lifecycle management across components
Installation
Build from source
make build
Or manually:
mkdir -p bin
go build -o bin/whatshook-server ./cmd/server
go build -o bin/whatshook-cli ./cmd/cli
Configuration
Create a config.json file based on the example:
cp config.example.json config.json
Edit the configuration file to add your WhatsApp accounts and webhooks:
{
"server": {
"host": "localhost",
"port": 8080,
"default_country_code": "27"
},
"whatsapp": [
{
"id": "account1",
"phone_number": "+1234567890",
"session_path": "./sessions/account1"
}
],
"hooks": [
{
"id": "hook1",
"name": "My Webhook",
"url": "https://example.com/webhook",
"method": "POST",
"headers": {
"Authorization": "Bearer token"
},
"active": true,
"description": "Webhook description"
}
],
"log_level": "info"
}
Configuration Options
Server Configuration:
host: Server hostname (default: "localhost")port: Server port (default: 8080)default_country_code: Default country code for phone number formatting (e.g., "27" for South Africa, "1" for US/Canada)username: Username for HTTP Basic Authentication (optional)password: Password for HTTP Basic Authentication (optional)auth_key: API key for x-api-key header or Authorization Bearer token authentication (optional)
WhatsApp Account Configuration:
id: Unique identifier for this accountphone_number: Phone number with country codesession_path: Path to store session data
Hook Configuration:
id: Unique identifier for this hookname: Human-readable nameurl: Webhook URL to callmethod: HTTP method (usually "POST")headers: Optional HTTP headersactive: Whether this hook is enableddescription: Optional description
Server Authentication
The server supports two authentication methods to protect API endpoints:
1. HTTP Basic Authentication
Set both username and password in the server configuration:
{
"server": {
"host": "localhost",
"port": 8080,
"username": "admin",
"password": "secure_password"
}
}
Clients must provide credentials in the Authorization header:
curl -u admin:secure_password http://localhost:8080/api/hooks
2. API Key Authentication
Set auth_key in the server configuration:
{
"server": {
"host": "localhost",
"port": 8080,
"auth_key": "your-secret-api-key"
}
}
Clients can provide the API key using either:
- x-api-key header:
curl -H "x-api-key: your-secret-api-key" http://localhost:8080/api/hooks - Authorization Bearer token:
curl -H "Authorization: Bearer your-secret-api-key" http://localhost:8080/api/hooks
Authentication Notes
- If no authentication is configured (all fields empty), the server operates without authentication
- The
/healthendpoint is always accessible without authentication - All
/api/*endpoints require authentication when enabled - Both authentication methods can be configured simultaneously - the server will accept either valid credentials or a valid API key
Usage
Starting the Server
./bin/whatshook-server -config config.json
On first run, you'll need to pair your WhatsApp account. The QR code will be displayed directly in the terminal for easy scanning:
========================================
WhatsApp QR Code for account: account1
Phone: +1234567890
========================================
Scan this QR code with WhatsApp on your phone:
[QR CODE DISPLAYED HERE]
========================================
The QR code is also published as an event (whatsapp.qr.code) so you can handle it programmatically if needed.
Using the CLI
The CLI uses Cobra and supports configuration from multiple sources with the following priority:
- Command-line flags (highest priority)
- Environment variables
- Configuration file (lowest priority)
Configuration
Create a CLI configuration file (optional):
cp .whatshooked-cli.example.json .whatshooked-cli.json
Or set via environment variable:
export WHATSHOOKED_SERVER_URL=http://localhost:8080
Or use command-line flag:
./bin/whatshook-cli --server http://localhost:8080 health
Commands
Get help:
./bin/whatshook-cli --help
./bin/whatshook-cli hooks --help
Check server health:
./bin/whatshook-cli health
List all hooks:
./bin/whatshook-cli hooks list
# or just
./bin/whatshook-cli hooks
Add a new hook:
./bin/whatshook-cli hooks add
Remove a hook:
./bin/whatshook-cli hooks remove <hook_id>
List WhatsApp accounts:
./bin/whatshook-cli accounts list
# or just
./bin/whatshook-cli accounts
Add a WhatsApp account:
./bin/whatshook-cli accounts add
Send a message:
./bin/whatshook-cli send
Configuration Priority
The CLI loads configuration with the following priority (highest to lowest):
- Command-line flags:
--server http://example.com:8080 - Environment variables:
WHATSHOOKED_SERVER_URL=http://example.com:8080 - Config file:
.whatshooked-cli.jsonin current directory or$HOME/.whatshooked/cli.json - Defaults:
http://localhost:8080
Webhook Integration
Incoming Message Format
When a WhatsApp message is received, all active webhooks receive a POST request with the following JSON payload:
{
"account_id": "account1",
"message_id": "3EB0123456789ABCDEF",
"from": "1234567890@s.whatsapp.net",
"to": "9876543210@s.whatsapp.net",
"text": "Hello, World!",
"timestamp": "2025-12-28T10:30:00Z",
"is_group": false,
"group_name": "",
"sender_name": ""
}
Webhook Response Format
Webhooks can respond with a JSON payload to send a message back to WhatsApp:
{
"send_message": true,
"to": "0834606792",
"text": "This is a response message",
"account_id": "account1"
}
Or using full JID format:
{
"send_message": true,
"to": "27834606792@s.whatsapp.net",
"text": "This is a response message",
"account_id": "account1"
}
Fields:
send_message: Set totrueto send a messageto: Recipient phone number or JID. Can be:- Plain phone number (e.g.,
"0834606792") - will be formatted usingdefault_country_code - Phone number with country code (e.g.,
"27834606792") - Full JID format (e.g.,
"27834606792@s.whatsapp.net")
- Plain phone number (e.g.,
text: Message text to sendaccount_id: (Optional) Which WhatsApp account to use. If not specified, uses the account that received the original message
Phone Number Formatting
The server automatically formats phone numbers to WhatsApp JID format:
- If the number contains
@, it's used as-is (already in JID format) - Otherwise, formatting rules apply:
- Removes all non-digit characters (spaces, dashes, parentheses, etc.)
- If starts with 0: Assumes no country code and replaces the 0 with the
default_country_code - If starts with +: Assumes it already has a country code
- Otherwise: Adds country code if configured and not already present
- Appends
@s.whatsapp.netsuffix
Examples with default_country_code: "27":
0834606792→27834606792@s.whatsapp.net(replaces leading 0 with 27)083-460-6792→27834606792@s.whatsapp.net(removes dashes, replaces 0)27834606792→27834606792@s.whatsapp.net(already has country code)+27834606792→27834606792@s.whatsapp.net(+ indicates country code present)27834606792@s.whatsapp.net→27834606792@s.whatsapp.net(unchanged, already JID)
API Endpoints
The server exposes the following HTTP endpoints:
GET /health- Health check (no authentication required)GET /api/hooks- List all hooks (requires authentication if enabled)POST /api/hooks/add- Add a new hook (requires authentication if enabled)POST /api/hooks/remove- Remove a hook (requires authentication if enabled)GET /api/accounts- List all WhatsApp accounts (requires authentication if enabled)POST /api/accounts/add- Add a new WhatsApp account (requires authentication if enabled)POST /api/send- Send a message (requires authentication if enabled)POST /api/send/image- Send an image (requires authentication if enabled)POST /api/send/video- Send a video (requires authentication if enabled)POST /api/send/document- Send a document (requires authentication if enabled)GET /api/media/{accountID}/{filename}- Serve media files (requires authentication if enabled)
WhatsApp JID Format
WhatsApp uses JID (Jabber ID) format for addressing:
- Individual:
1234567890@s.whatsapp.net - Group:
123456789-1234567890@g.us
The server accepts both full JID format and plain phone numbers. When using plain phone numbers, they are automatically formatted to JID format based on the default_country_code configuration. See Phone Number Formatting for details.
Development
Project Structure
whatshooked/
├── cmd/
│ ├── server/ # Main server application
│ └── cli/ # CLI tool
├── internal/
│ ├── config/ # Configuration management
│ ├── events/ # Event bus and event types
│ ├── logging/ # Structured logging
│ ├── whatsapp/ # WhatsApp client management
│ ├── hooks/ # Webhook management
│ └── utils/ # Utility functions (phone formatting, etc.)
├── config.example.json # Example configuration
└── go.mod # Go module definition
Event Types
The system publishes the following event types:
WhatsApp Events:
whatsapp.connected- WhatsApp client connectedwhatsapp.disconnected- WhatsApp client disconnectedwhatsapp.pair.success- Device pairing successfulwhatsapp.pair.failed- Device pairing failedwhatsapp.qr.code- QR code generated for pairing (includes qr_code data)whatsapp.qr.timeout- QR code expiredwhatsapp.qr.error- QR code generation errorwhatsapp.pair.event- Generic pairing event
Message Events:
message.received- New message received from WhatsAppmessage.sent- Message successfully sent to WhatsAppmessage.failed- Message send failed
Hook Events:
hook.triggered- Webhook is being calledhook.success- Webhook responded successfullyhook.failed- Webhook call failed
Testing
go test ./...
Building
go build ./...
Future Phases
Phase 2 (Planned)
- User level hooks and WhatsApp accounts
- Web server with frontend UI
- Enhanced authentication with user roles and permissions
License
See LICENSE file for details.
