@jimiford/webex
v0.1.3
Published
OpenClaw channel plugin for Cisco Webex messaging
Maintainers
Readme
@jimiford/webex
OpenClaw channel plugin for Cisco Webex messaging. Enables your OpenClaw gateway to send and receive messages via Webex bots.
Features
- Direct Messages (1:1): Send and receive private messages with users
- Space/Room Messages: Communicate in Webex spaces and rooms
- Attachments: Send files via public URLs
- Adaptive Cards: Rich interactive message cards
- Threaded Replies: Support for message threading
- Webhook Integration: Real-time message reception
- Automatic Retries: Configurable retry logic with exponential backoff
- Message Normalization: Converts Webex messages to OpenClaw's envelope format
Installation
npm install @jimiford/webexPrerequisites
1. Create a Webex Bot
- Go to Webex Developer Portal
- Sign in with your Webex account
- Navigate to My Webex Apps → Create a New App
- Select Create a Bot
- Fill in the bot details:
- Bot Name: Your bot's display name
- Bot Username: Unique identifier (e.g.,
[email protected]) - Icon: Upload or select an icon
- Description: Brief description of your bot
- Click Add Bot
- Important: Copy the Bot Access Token - you'll only see it once!
2. Set Up a Public Webhook URL
Your webhook endpoint must be publicly accessible. Options:
- Production: Deploy to a cloud provider with HTTPS
- Development: Use ngrok to expose localhost:
ngrok http 3000
Configuration
import { createWebexChannel, WebexChannelConfig } from '@jimiford/webex';
const config: WebexChannelConfig = {
// Required: Your Webex bot access token
token: 'YOUR_BOT_ACCESS_TOKEN',
// Required: Public URL for receiving webhooks
webhookUrl: 'https://your-domain.com/webhooks/webex',
// Required: Policy for handling direct messages
// - 'allow': Accept DMs from anyone
// - 'deny': Reject all DMs
// - 'allowlisted': Only accept from specified users
dmPolicy: 'allow',
// Optional: List of allowed person IDs or emails (when dmPolicy is 'allowlisted')
allowFrom: ['[email protected]', 'Y2lzY29zcGFyazov...'],
// Optional: Secret for webhook signature verification
webhookSecret: 'your-webhook-secret',
// Optional: Custom API base URL (default: https://webexapis.com/v1)
apiBaseUrl: 'https://webexapis.com/v1',
// Optional: Maximum retry attempts (default: 3)
maxRetries: 3,
// Optional: Retry delay in ms (default: 1000)
retryDelayMs: 1000,
};Usage
Basic Setup
import { createWebexChannel } from '@jimiford/webex';
async function main() {
// Create and initialize the channel
const channel = createWebexChannel();
await channel.initialize({
token: process.env.WEBEX_BOT_TOKEN!,
webhookUrl: process.env.WEBHOOK_URL!,
dmPolicy: 'allow',
});
// Register webhooks with Webex
await channel.registerWebhooks();
// Register a message handler
channel.onMessage(async (envelope) => {
console.log('Received message:', envelope);
// Echo the message back
await channel.send({
to: envelope.conversationId,
content: { text: `You said: ${envelope.content.text}` },
});
});
console.log('Webex channel ready!');
}
main().catch(console.error);Sending Messages
// Send to a room
await channel.sendText('roomId', 'Hello, room!');
// Send markdown
await channel.sendMarkdown('roomId', '**Bold** and _italic_');
// Send direct message
await channel.sendDirect('[email protected]', 'Hello!');
// Reply in a thread
await channel.reply('roomId', 'parentMessageId', 'This is a reply');
// Send with full options
await channel.send({
to: 'roomId',
content: {
text: 'Plain text fallback',
markdown: '**Rich** content',
files: ['https://example.com/image.png'],
},
parentId: 'threadParentId',
});Handling Webhooks
Set up an HTTP endpoint to receive webhooks:
import express from 'express';
import { createWebexChannel } from '@jimiford/webex';
const app = express();
app.use(express.json());
const channel = createWebexChannel();
// Initialize channel (do this on startup)
await channel.initialize({
token: process.env.WEBEX_BOT_TOKEN!,
webhookUrl: 'https://your-domain.com/webhooks/webex',
dmPolicy: 'allow',
webhookSecret: process.env.WEBHOOK_SECRET,
});
// Webhook endpoint
app.post('/webhooks/webex', async (req, res) => {
try {
const signature = req.headers['x-spark-signature'] as string;
const envelope = await channel.handleWebhook(req.body, signature);
if (envelope) {
// Process the message
console.log('Message from:', envelope.author.email);
console.log('Content:', envelope.content.text);
}
res.status(200).send('OK');
} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('Error');
}
});
app.listen(3000);OpenClaw Envelope Format
Incoming messages are normalized to this format:
interface OpenClawEnvelope {
id: string; // Webex message ID
channel: 'webex'; // Channel identifier
conversationId: string; // Room ID
author: {
id: string; // Person ID
email?: string; // Email address
displayName?: string; // Display name
isBot: boolean; // Always false (bot messages filtered)
};
content: {
text?: string; // Plain text content
markdown?: string; // Markdown content
attachments?: Array<{
type: 'file' | 'card';
url?: string; // File URL
content?: unknown; // Card content
}>;
};
metadata: {
roomType: 'direct' | 'group';
roomId: string;
timestamp: string; // ISO 8601
mentions?: string[]; // Mentioned person IDs
parentId?: string; // Thread parent message ID
raw: WebexMessage; // Original Webex message
};
}Advanced Usage
Direct Sender Access
const sender = channel.getSender();
// Get message details
const message = await sender.getMessage('messageId');
// Delete a message
await sender.deleteMessage('messageId');Direct Webhook Handler Access
const webhookHandler = channel.getWebhookHandler();
// List existing webhooks
const webhooks = await webhookHandler.listWebhooks();
// Delete a webhook
await webhookHandler.deleteWebhook('webhookId');Error Handling
import { WebexApiRequestError } from '@jimiford/webex';
try {
await channel.send({ to: 'invalid', content: { text: 'test' } });
} catch (error) {
if (error instanceof WebexApiRequestError) {
console.error('API Error:', error.message);
console.error('Status:', error.statusCode);
console.error('Tracking ID:', error.trackingId);
}
}API Reference
WebexChannel
| Method | Description |
|--------|-------------|
| initialize(config) | Initialize with configuration |
| send(message) | Send a message |
| sendText(roomId, text) | Send plain text to a room |
| sendMarkdown(roomId, md) | Send markdown to a room |
| sendDirect(to, text) | Send direct message |
| reply(roomId, parentId, text) | Send threaded reply |
| handleWebhook(payload, sig?) | Process incoming webhook |
| onMessage(handler) | Register message handler |
| offMessage(handler) | Remove message handler |
| registerWebhooks() | Register webhooks with Webex |
| shutdown() | Cleanup and shutdown |
Environment Variables
Recommended environment variables:
WEBEX_BOT_TOKEN=your_bot_access_token
WEBHOOK_URL=https://your-domain.com/webhooks/webex
WEBHOOK_SECRET=your_webhook_secretTroubleshooting
Bot not receiving messages
- Ensure webhooks are registered:
await channel.registerWebhooks() - Verify your webhook URL is publicly accessible
- Check that the bot is added to the room/space
- For DMs, the user must message the bot first
"Invalid webhook signature" errors
- Ensure
webhookSecretmatches the secret used when creating webhooks - Verify the signature header name:
x-spark-signature
Rate limiting
The plugin includes automatic retry with exponential backoff for rate-limited requests. Adjust maxRetries and retryDelayMs in config if needed.
Security Considerations
When connecting a Webex bot to OpenClaw, keep these security implications in mind:
Access Control
- DM Policy: The
dmPolicysetting controls who can interact with your bot:allow: Anyone can message the bot and receive responses (use with caution)deny: The bot won't respond to direct messagesallowlisted: Only users in theallowFromlist receive responses
- Recommendation: Use
allowlistedin production and explicitly specify trusted users
Bot Token Permissions
- The bot access token can read messages sent to the bot and send replies
- Keep your token secret — never commit it to version control
- Rotate tokens periodically via the Webex Developer Portal
Webhook Security
- Always use a webhook secret in production to verify incoming requests
- The
webhookSecretenables HMAC-SHA1 signature verification - Without verification, attackers could send fake webhook payloads to your endpoint
Network Exposure
- Your webhook endpoint must be publicly accessible for Webex to deliver messages
- Use HTTPS in production (required by Webex)
- Consider IP allowlisting if your infrastructure supports it
- For development, tools like ngrok create temporary public URLs
OpenClaw Agent Access
- Messages received by the bot flow through your OpenClaw agent
- The agent has access to whatever tools you've configured (file access, web browsing, etc.)
- Treat bot conversations with the same security considerations as direct OpenClaw access
- Review your agent's tool permissions and workspace access
Best Practices
- Start with
dmPolicy: 'deny'ordmPolicy: 'allowlisted'and explicitly allow trusted users - Always configure a
webhookSecretfor production deployments - Monitor bot activity through Webex admin tools and OpenClaw logs
- Use separate bots for development and production environments
- Regularly audit the
allowFromlist
License
MIT
