We build custom applications 5x faster and cheaper 🚀
Book a Free Consultation
Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.
Install the Zoho CRM integration as a skill through ClawHub, implement Zoho OAuth 2.0 (create a client in Zoho, run an OAuth authorization/code flow to get access + refresh tokens), store those credentials securely in your ClawHub/secret store or an external secret manager, and have the skill call Zoho’s REST API (https://www.zohoapis.com/crm/v2/...) using the access token. Put any stateful or long-running pieces (OAuth callback endpoint, token refresh, webhook receiver, retry queues, persistent logs) outside the agent runtime (a small web service or cloud function), and have the OpenClaw skill invoke that service or read secrets to perform short authenticated API requests. Validate webhooks per Zoho’s docs, respect OAuth scopes, and debug by inspecting API responses, logs, and token lifetimes.
# <b>//</b> Example token exchange (replace domain if needed)
curl -X POST "https://accounts.zoho.com/oauth/v2/token" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "redirect_uri=https://your-callback.example.com/oauth/callback" \
-d "code=AUTHORIZATION_CODE"
// <b>//</b> Node.js express minimal callback handler
import express from 'express';
import fetch from 'node-fetch';
const app = express();
app.get('/oauth/callback', async (req, res) => {
// <b>//</b> req.query.code is the authorization code from Zoho
const code = req.query.code;
if (!code) return res.status(400).send('missing code');
const params = new URLSearchParams();
params.append('grant_type', 'authorization_code');
params.append('client_id', process.env.ZOHO_CLIENT_ID);
params.append('client_secret', process.env.ZOHO_CLIENT_SECRET);
params.append('redirect_uri', process.env.ZOHO_REDIRECT_URI);
params.append('code', code);
const tokenResp = await fetch('https://accounts.zoho.com/oauth/v2/token', {
method: 'POST',
body: params
});
const tokenJson = await tokenResp.json();
// <b>//</b> save tokenJson.refresh_token and tokenJson.access_token securely
// <b>//</b> persist to a DB or secrets manager; do not keep long-term in memory
// Example: await saveTokensToDB({refresh_token: tokenJson.refresh_token, access_token: tokenJson.access_token});
res.send('OAuth completed; store refresh token for skills');
});
app.listen(3000);
# <b>//</b> Example: create a Lead with curl
curl -X POST "https://www.zohoapis.com/crm/v2/Leads" \
-H "Authorization: Zoho-oauthtoken ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"Last_Name": "Doe",
"First_Name": "Jane",
"Company": "Acme Inc."
}
]
}'
# <b>//</b> Example refresh token exchange
curl -X POST "https://accounts.zoho.com/oauth/v2/token" \
-d "grant_type=refresh_token" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "refresh_token=YOUR_REFRESH_TOKEN"
Speak one‑on‑one with a senior engineer about your no‑code app, migration goals, and budget. In just half an hour you’ll leave with clear, actionable next steps—no strings attached.
1
Direct answer: An invalid_grant from Zoho means the refresh token the OpenClaw Zoho CRM Connector is presenting is rejected — common causes are a revoked/expired token, a mismatched client_id/client_secret or redirect URI, using the wrong grant_type (e.g., sending an auth code), token rotation/replace, or storing the wrong value in environment variables. In OpenClaw you must ensure the skill’s env vars hold the current refresh token and client credentials and that the runtime uses them when calling Zoho’s token endpoint.
fetch('https://accounts.zoho.com/oauth/v2/token', {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'},
body: new URLSearchParams({
client_id: process.env.ZOHO_CLIENT_ID,
client_secret: process.env.ZOHO_CLIENT_SECRET,
refresh_token: process.env.ZOHO_REFRESH_TOKEN,
grant_type: 'refresh_token'
})
})
.then(r => r.json())
.then(data => console.log(data))
.catch(err => console.error(err))
//check response for error and inspect error_description2
Most likely the Mapping Rules are not being executed or their field names/conditions don’t match Zoho’s API names, or the skill lacks proper Zoho OAuth scopes/credentials so updates fail silently. Check execution, API field names, and auth first.
// Fetch Zoho module fields to verify API names
fetch('https://www.zohoapis.com/crm/v2/settings/fields?module=Leads', {
headers: { 'Authorization': 'Zoho-oauthtoken ' + process.env.ZOHO_TOKEN }
})
.then(res => res.json())
.then(console.log)
3
Zoho typically rejects an OpenClaw webhook subscription when the validation token or signature you expect doesn't match what Zoho sends, or when your endpoint returns a 403 because of auth, firewall, TLS or permission issues. Fix by verifying the exact token/signature exchange and that the OpenClaw skill endpoint is reachable and authorized.
4
Duplicates happen when OpenClaw doesn’t send Zoho’s External ID (so Zoho can’t match incoming data to an existing record) or when the connector’s Conflict Resolution is configured to always create instead of upserting/updating. In short: missing external key or wrong conflict policy causes Zoho to treat every incoming record as new.
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â