| name | openclaw-cliq-integration |
| description | SprintCX OpenClaw Channel Plugin for Zoho Cliq — connect OpenClaw agents to Zoho Cliq with bot-per-agent, markdown conversion, streaming, and group chat support |
| triggers | ["integrate openclaw with zoho cliq","set up cliq channel for openclaw agent","configure zoho cliq bot for openclaw","connect my openclaw gateway to cliq","enable cliq messaging in openclaw","troubleshoot openclaw cliq webhook","implement zoho cliq channel plugin","configure cliq oauth for openclaw"] |
OpenClaw Cliq Integration
Skill by ara.so — Hermes Skills collection.
This skill covers the openclaw-cliq channel plugin that connects OpenClaw agents to Zoho Cliq for DMs, channel @mentions, streaming previews, cards, buttons, and message actions. The plugin implements OAuth 2.0, multi-data-center support, and handles both direct messages (via client_credentials) and channel posts/edits (via user-context refresh token).
What It Does
- Messaging: Handles DMs and channel @mentions via Deluge webhook, supports image/file/voice attachments
- Rich Replies: Markdown → Cliq formatting, live-edit streaming, interactive cards/buttons
- Message Actions: Edit, delete, react to sent messages from the agent
- Security: DM allowlist/pairing policies, per-channel tool policies, hardened webhook with rate limiting
- OAuth: Dual-grant OAuth 2.0 (client_credentials for DMs, refresh token for channels)
- Multi-Region: Works across all Zoho data centers (US, EU, IN, AU, JP, CA, SA, CN)
Installation
openclaw plugins install clawhub:@sprintcx/openclaw-cliq
openclaw setup
openclaw status
openclaw channels
Zoho Cliq Setup
1. Create Bot in Cliq
- Profile picture → My Cliq → Bots & Tools → Bots → Create Bot
- Set Bot Name (display, e.g.,
OpenClaw Agent) and Bot Unique Name (e.g., openclaw_agent — this is your botId)
- Choose Custom Bot type
- Enable Mention Handler and Message Handler
- Publish/activate the bot
- Invite bot to channels where it should respond
2. Get OAuth Credentials
- Open Zoho API Console (use your data center's domain)
- Create Self Client or Server-based Application
- Note Client ID and Client Secret
- Consent these scopes:
ZohoCliq.Webhooks.CREATE (DMs)
ZohoCliq.Channels.UPDATE (channel posts)
ZohoCliq.Channels.CREATE (v3 cards)
ZohoCliq.Channels.READ (metadata)
ZohoCliq.Users.READ (user info)
ZohoCliq.Messages.UPDATE (edit/stream)
ZohoCliq.Messages.READ (attachments)
ZohoCliq.Messages.DELETE (delete)
ZohoCliq.messageactions.CREATE (reactions)
3. Generate Webhook Secret
openssl rand -hex 32
Save this as your webhookSecret.
4. Obtain Refresh Token (Required for Channels)
Generate authorization URL (replace placeholders):
const authUrl = `https://accounts.zoho.eu/oauth/v2/auth?scope=ZohoCliq.Channels.UPDATE,ZohoCliq.Messages.UPDATE,ZohoCliq.Messages.READ,ZohoCliq.Messages.DELETE,ZohoCliq.Channels.CREATE,ZohoCliq.messageactions.CREATE&client_id=${CLIENT_ID}&response_type=code&access_type=offline&redirect_uri=http://localhost:8080/callback&prompt=consent`;
Visit URL, consent, capture the code from redirect, then exchange:
curl -X POST https://accounts.zoho.eu/oauth/v2/token \
-d "grant_type=authorization_code" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" \
-d "code=${CODE}" \
-d "redirect_uri=http://localhost:8080/callback"
Extract refresh_token from response.
5. Deluge Webhook Handler
Paste into bot's Message Handler and Mention Handler:
response = Map();
webhookUrl = "https://your-gateway.example.com/cliq/webhook";
webhookSecret = "your-webhook-secret-here";
headers = {"x-cliq-webhook-secret": webhookSecret, "Content-Type": "application/json"};
try {
webhookResp = invokeurl [
url: webhookUrl
type: POST
parameters: arguments
headers: headers
];
info "Webhook forwarded: " + webhookResp;
response = {"text": ""};
} catch (e) {
info "Webhook error: " + e;
response = {"text": "⚠️ Processing error"};
}
return response;
Replace webhookUrl with https://<your-gateway>/cliq/webhook and webhookSecret with your generated secret.
Configuration
Create ~/.openclaw/accounts/cliq-main.yaml (or use openclaw setup):
channel: cliq
enabled: true
botId: openclaw_agent
botName: OpenClaw Agent
clientId: ${ZOHO_CLIENT_ID}
clientSecret: ${ZOHO_CLIENT_SECRET}
refreshToken: ${ZOHO_REFRESH_TOKEN}
webhookSecret: ${CLIQ_WEBHOOK_SECRET}
oauthBase: https://accounts.zoho.eu
apiBase: https://cliq.zoho.eu
dmPolicy: pairing
channels:
- uniqueName: engineering
requireMention: true
admissionPolicy: open
apiVersion:
channelCard: v3
maxMessageLength: 8000
streamingMinInterval: 1000
outboundRetry:
maxAttempts: 3
backoffMs: 1000
Data Center Mapping
Set oauthBase and apiBase for your region:
| Region | OAuth Base | API Base |
|---|
| US | https://accounts.zoho.com | https://cliq.zoho.com |
| EU | https://accounts.zoho.eu | https://cliq.zoho.eu |
| IN | https://accounts.zoho.in | https://cliq.zoho.in |
| AU | https://accounts.zoho.com.au | https://cliq.zoho.com.au |
| JP | https://accounts.zoho.jp | https://cliq.zoho.jp |
| CA | https://accounts.zohocloud.ca | https://cliq.zohocloud.ca |
| SA | https://accounts.zoho.sa | https://cliq.zoho.sa |
| CN | https://accounts.zoho.com.cn | https://cliq.zoho.com.cn |
Key Commands
openclaw channels
openclaw status
openclaw directory --channel cliq-main --query alice@example.com
openclaw doctor cliq
openclaw setup
curl -X POST http://localhost:3000/cliq/webhook \
-H "x-cliq-webhook-secret: ${CLIQ_WEBHOOK_SECRET}" \
-H "Content-Type: application/json" \
-d '{
"bot": {"unique_name": "openclaw_agent"},
"message": {"text": "hello"},
"user": {"id": "123", "email": "user@example.com"},
"type": "message"
}'
Code Examples
Send DM (Client Credentials Flow)
const dmPayload = {
userids: ["user_id_123"],
text: "Hello from OpenClaw!"
};
Send Channel Message (Refresh Token Flow)
const channelPayload = {
text: "*Bold* and _italic_ markdown",
bot: {
name: "OpenClaw Agent",
image: "https://example.com/avatar.png"
}
};
Live-Edit Streaming
const msgId = await sendMessage(channelId, "Thinking...");
for await (const chunk of agentStream) {
await editMessage(chatId, msgId, chunk.text);
await sleep(1000);
}
Send Interactive Card (v3)
const card = {
bot: { name: "OpenClaw Agent" },
card: {
theme: "modern-inline",
thumbnail: "https://example.com/preview.png",
title: "Choose Action",
buttons: [
{
label: "Approve",
type: "+",
action: {
type: "invoke.function",
data: { action: "approve" }
}
}
],
slides: [
{
type: "text",
data: "Review the following details:"
},
{
type: "table",
data: {
headers: ["Field", "Value"],
rows: [["Status", "Pending"]]
}
}
]
}
};
Delete Message (v3 Bulk Delete)
const deletePayload = {
message_ids: [messageId]
};
Add Reaction
const reaction = {
emoji: "👍"
};
Handle Cliq Form Submission
{
"type": "form_submit",
"form": {
"name": "ParameterCapture",
"values": {
"project": "openclaw",
"priority": "high"
}
},
"user": { "email": "alice@example.com" }
}
DM Pairing Flow
const approvalCard = {
text: "alice@example.com wants to chat. Approve?",
card: {
buttons: [
{ label: "Approve", action: { type: "invoke.function", data: { action: "approve_dm", userId: "123" } } },
{ label: "Deny", action: { type: "invoke.function", data: { action: "deny_dm" } } }
]
}
};
Common Patterns
Multi-Account Setup
channel: cliq
botId: prod_agent
channel: cliq
botId: dev_agent
Per-Channel Tool Policy
channels:
- uniqueName: public-support
requireMention: true
toolPolicy:
allowedTools: ["search", "docs"]
deniedTools: ["admin", "deploy"]
userToolOverrides:
admin@example.com:
allowedTools: ["admin", "deploy"]
Secure Webhook Validation
if (!timingSafeEqual(Buffer.from(receivedSecret), Buffer.from(configSecret))) {
logFailedAuth(req.ip);
if (isRateLimited(req.ip)) {
return res.status(429).send("Too many failed attempts");
}
return res.status(401).send("Invalid webhook secret");
}
Attachment Download
Channel Admission Control
channels:
- uniqueName: restricted-channel
admissionPolicy: allowlist
userAllowlist:
- alice@example.com
- bob@example.com
requireMention: true
Troubleshooting
"oauthtoken_scope_invalid" on Channel Post
Symptom: DMs work, channel posts fail with {"code":"oauthtoken_scope_invalid"}
Cause: Missing or expired refresh token. The client_credentials grant cannot obtain ZohoCliq.Channels.UPDATE scope.
Fix: Complete §3c to obtain a refresh token, set it in config:
refreshToken: ${ZOHO_REFRESH_TOKEN}
Webhook Not Receiving Events
Checklist:
- Bot is published/active in Cliq
- Bot is invited to the channel (for mentions)
- Deluge handler is deployed to Message/Mention handlers
- Webhook URL is publicly accessible (test with
curl from external host)
webhookSecret matches between Deluge and plugin config
- Check OpenClaw gateway logs:
openclaw logs --filter cliq
Live Streaming Not Working
Symptom: Messages appear as separate posts, not edits
Requirements:
refreshToken configured (edit uses Messages.UPDATE scope)
streamingMinInterval respected (default 1000ms, Cliq rate-limits edits)
- Message ID returned from initial send and cached
Debug:
Wrong Data Center
Symptom: OAuth/API calls fail with 4xx/5xx, or token requests hang
Fix: Set correct oauthBase and apiBase in config (see Data Center Mapping)
oauthBase: https://accounts.zoho.com.au
apiBase: https://cliq.zoho.com.au
Rate Limiting
Zoho Cliq API limits: ~10 req/sec per endpoint
Plugin mitigation:
- Token caching (1h TTL, reused across requests)
- Edit throttling (
streamingMinInterval)
- Exponential backoff on 429 (configurable via
outboundRetry)
Message Too Long
Limit: 8000 chars (configurable via maxMessageLength)
Plugin behavior: Auto-chunks long messages into multiple posts
maxMessageLength: 8000
Bot Loop Protection
Symptom: Bot replies to itself infinitely
Built-in protection: Plugin filters out messages where user.id === bot.id or user.email matches bot owner.
Manual: Add to Deluge handler:
if (arguments.get("user").get("id") == arguments.get("bot").get("id")) {
return {"text": ""};
}
Form Submissions Not Recognized
Requirements:
- Bot has Form Handler defined in Cliq
- Webhook forwards
type: "form_submit" events
form.name and form.values present in payload
Debug:
openclaw logs --filter cliq --level debug
File Attachments Not Downloading
Requirements:
ZohoCliq.Messages.READ scope consented
- Refresh token configured
- Chat ID available (DM chats return
chatId in webhook; channel messages need lookup)
Fallback: Without Messages.READ, attachments surface as name-only (no bytes reach agent).
References