Put the same agent on Telegram, Discord, and WebSocket simultaneously.
GoClaw runs multiple channels from one gateway process. A single agent can receive messages from Telegram, Discord, and direct WebSocket clients at the same time — each channel has its own session scope, so conversations stay isolated per channel and user.
What you need:
- A working gateway with at least one agent created
- Web dashboard access at
http://localhost:18790 - Bot tokens for each messaging platform
You need a bot token for each messaging platform:
Telegram: Message @BotFather → /newbot → copy token
Discord: discord.com/developers → New Application → Bot → Add Bot → copy token. Enable Message Content Intent under Privileged Gateway Intents.
WebSocket needs no external token — clients authenticate with your gateway token.
Open the web dashboard and go to Channels → Create Instance. Create one instance per platform:
Telegram:
- Channel type: Telegram
- Name:
main-telegram - Agent: Select your agent
- Credentials: Paste the bot token from @BotFather
- Config: Set
dm_policytopairing(recommended) oropen
Click Save.
Discord:
- Channel type: Discord
- Name:
main-discord - Agent: Select the same agent
- Credentials: Paste the Discord bot token
- Config: Set
dm_policytoopen,require_mentiontotrue
Click Save.
Both channels are immediately active — no gateway restart needed. WebSocket is built into the gateway and needs no instance creation.
On startup you should see log lines like:
channel=telegram status=connected bot=@YourBotName
channel=discord status=connected guild_count=2
gateway status=listening addr=0.0.0.0:18790
Via config.json
Add all channel configs to config.json. Secrets (tokens) go in .env.local — not in the config file.
config.json:
{
"channels": {
"telegram": {
"enabled": true,
"token": "",
"dm_policy": "pairing",
"group_policy": "open",
"require_mention": true,
"reaction_level": "minimal"
},
"discord": {
"enabled": true,
"token": "",
"dm_policy": "open",
"group_policy": "open",
"require_mention": true,
"history_limit": 50
}
},
"gateway": {
"host": "0.0.0.0",
"port": 18790,
"token": ""
}
}.env.local (secrets only — never commit this file):
export GOCLAW_TELEGRAM_TOKEN="123456:ABCDEFGHIJKLMNOPQRSTUVWxyz"
export GOCLAW_DISCORD_TOKEN="your-discord-bot-token"
export GOCLAW_GATEWAY_TOKEN="your-gateway-token"
export GOCLAW_POSTGRES_DSN="postgres://user:pass@localhost:5432/goclaw"GoClaw reads channel tokens from environment variables when the token field in config is empty.
Add bindings to route messages to your agent:
{
"bindings": [
{
"agentId": "my-assistant",
"match": { "channel": "telegram" }
},
{
"agentId": "my-assistant",
"match": { "channel": "discord" }
}
]
}Start the gateway:
source .env.local && ./goclawWebSocket is built into the gateway — no extra setup needed. Connect and authenticate:
const ws = new WebSocket('ws://localhost:18790/ws');
// First frame must be connect
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'req',
id: '1',
method: 'connect',
params: {
token: 'your-gateway-token',
user_id: 'web-user-alice'
}
}));
};
// Send a chat message
function chat(message) {
ws.send(JSON.stringify({
type: 'req',
id: String(Date.now()),
method: 'chat',
params: {
agent: 'my-assistant',
message: message
}
}));
}
// Listen for responses and streaming chunks
ws.onmessage = (e) => {
const frame = JSON.parse(e.data);
if (frame.type === 'event' && frame.event === 'chunk') {
process.stdout.write(frame.payload.text);
}
if (frame.type === 'res' && frame.method === 'chat') {
console.log('\n[done]');
}
};See WebSocket Channel for the full protocol reference.
Sessions are isolated by channel and user by default (dm_scope: "per-channel-peer"). This means:
- Alice on Telegram and Alice on Discord have separate conversation histories
- The agent treats them as different users
Verify isolation in the dashboard: go to Sessions and filter by agent — you should see separate sessions for each channel.
If you want a single session across channels for the same user, set dm_scope: "per-peer" in config.json:
{
"sessions": {
"dm_scope": "per-peer"
}
}This shares conversation history when the same user_id connects from any channel.
Telegram has a 4096-character message limit. GoClaw handles long responses automatically:
- Long messages are split into multiple parts at natural boundaries (paragraphs, code blocks)
- HTML formatting is attempted first for rich output
- If HTML parsing fails, the message falls back to plain text
- No configuration needed — this is fully automatic
| Feature | Telegram | Discord | WebSocket |
|---|---|---|---|
| Setup | @BotFather token | Developer Portal token | None (use gateway token) |
| DM policy default | pairing |
open |
Auth via gateway token |
| Group/server support | Yes | Yes | N/A |
| Streaming | Optional (dm_stream) |
Via message edits | Native (chunk events) |
| Mention required in groups | Yes (default) | Yes (default) | N/A |
| Custom client | No | No | Yes |
You can allow different tool sets per channel. Go to Agents → your agent → Config tab and configure per-channel tool policies.
Via config.json
{
"agents": {
"list": {
"my-assistant": {
"tools": {
"byProvider": {
"telegram": { "deny": ["exec", "write_file"] },
"discord": { "deny": ["exec", "write_file"] }
}
}
}
}
}
}WebSocket clients (usually developers or internal tools) can keep full tool access.
When the agent uses write_file to generate a file, it is automatically delivered as a channel attachment. This works across Telegram, Discord, and other supported channels — no extra configuration needed.
| Problem | Solution |
|---|---|
| Telegram bot not responding | Check dm_policy. Default is "pairing" — complete browser pairing first, or set "open" for testing. |
| Discord bot offline in server | Verify the bot has been added to the server via OAuth2 URL Generator with bot scope and Send Messages permission. |
| WebSocket connect rejected | Ensure token in your connect frame matches GOCLAW_GATEWAY_TOKEN. Empty token gives viewer-only role. |
| Messages routing to wrong agent | Check channel instance agent assignment in Dashboard → Channels. First matching binding wins when using config.json. |
| Same user gets different sessions on Telegram vs Discord | Expected with default dm_scope: "per-channel-peer". Set "per-peer" to share sessions across channels. |
- Telegram Channel — full Telegram config reference including groups, topics, and STT
- Discord Channel — Discord gateway intents and streaming setup
- WebSocket Channel — full RPC protocol reference
- Personal Assistant — single-channel starting point