Zoho CRM’s OAuth 2.0 flow is more involved than a simple API key, and that trips up most people trying to connect it to an AI agent. The self-client method cuts through the complexity: no redirect URIs, no web server, just a grant token you exchange for persistent API access. Once that is in place, writing an OpenClaw skill that talks to Zoho takes about 15 minutes.
This guide covers the full process: registering a self-client in Zoho’s API Console, generating and exchanging tokens, writing an OpenClaw skill file with working curl templates for leads, contacts, and deals, and testing the integration end to end. If OpenClaw is already running on your machine, expect to finish in about 25 minutes.
What This Integration Does
A connected OpenClaw agent becomes a conversational front end to your Zoho CRM. Instead of navigating Zoho’s UI to check a deal stage or log a call, you message your agent and it handles the REST API calls directly.
What you get out of the box:
- Search leads, contacts, and deals by name, email, phone, or any custom field
- Create and update records across Zoho CRM modules without opening a browser
- Move deals through pipeline stages from a Telegram or WhatsApp message
- Log notes and follow-up tasks tied to specific contacts or deals
- Run daily CRM summaries via OpenClaw’s heartbeat scheduling
The agent calls Zoho’s REST API v6 directly. No third-party middleware, no paid connector platforms, no Zapier tax. Your data moves between your machine and Zoho’s servers with nothing in between.
Before You Start
Three things need to be in place:
-
OpenClaw installed and running. If you have not set this up yet, follow our OpenClaw setup guide. That covers installation, workspace files, memory, and Telegram configuration.
-
A Zoho CRM account with API access. The Free edition includes 5,000 API credits per day, which is enough for normal conversational use. Standard and above get 100,000+ daily credits.
-
A text editor. VS Code, Cursor, or anything that handles Markdown.
You also need to know which Zoho data center hosts your account. This determines the API domain for all requests:
| Data Center | Accounts URL | API Domain |
|---|---|---|
| US | accounts.zoho.com | www.zohoapis.com |
| EU | accounts.zoho.eu | www.zohoapis.eu |
| India | accounts.zoho.in | www.zohoapis.in |
| Australia | accounts.zoho.com.au | www.zohoapis.com.au |
| Japan | accounts.zoho.jp | www.zohoapis.jp |
Check your browser’s address bar when logged into Zoho. If the URL contains .eu, you are on the EU data center. If it is plain .com, you are on US. Use the matching domain throughout this guide.
Step 1: Register a Self-Client in Zoho API Console
Zoho offers several OAuth client types: web apps, mobile apps, and self-clients. For backend integrations where you own the Zoho account, self-client is the right choice. It requires no redirect URI and no web server.
- Go to Zoho API Console and sign in with your Zoho account
- Click Add Client
- Select Self Client and click Create Now
- Give it a name:
OpenClaw CRM Integration - Copy your Client ID and Client Secret immediately
Store both values in a .env file:
# ~/.env or ~/openclaw/.env
ZOHO_CLIENT_ID=1000.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ZOHO_CLIENT_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
These credentials do not expire, but keep them out of any file your agent might read into context or share.
Step 2: Generate a Grant Token
The grant token is a short-lived authorization code that you exchange for access and refresh tokens. It expires in 3 minutes, so have your terminal ready before generating it.
In the API Console, go to your Self Client and open the Generate Code tab.
- Enter the scopes your integration needs. For a full CRM integration, use:
ZohoCRM.modules.leads.ALL,ZohoCRM.modules.contacts.ALL,ZohoCRM.modules.deals.ALL,ZohoCRM.modules.tasks.ALL,ZohoCRM.modules.notes.ALL,ZohoCRM.users.READ,ZohoCRM.settings.ALL
- Set the time duration (leave it at 3 minutes unless you need longer)
- Enter a scope description:
OpenClaw CRM automation - Click Create
- Select your Zoho CRM portal when prompted
- Copy the generated code immediately
The scope string above gives your agent full access to leads, contacts, deals, tasks, and notes, plus read access to user data and CRM settings. If you only want read access initially, replace .ALL with .READ for each module.
Step 3: Exchange the Grant Token for Access and Refresh Tokens
Within 3 minutes of generating the grant token, run this curl command. Replace the placeholders with your actual values and use the correct accounts URL for your data center:
curl -X POST "https://accounts.zoho.com/oauth/v2/token" \
-d "grant_type=authorization_code" \
-d "client_id=$ZOHO_CLIENT_ID" \
-d "client_secret=$ZOHO_CLIENT_SECRET" \
-d "code=PASTE_YOUR_GRANT_TOKEN_HERE"
A successful response looks like this:
{
"access_token": "1000.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"refresh_token": "1000.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"api_domain": "https://www.zohoapis.com",
"token_type": "Bearer",
"expires_in": 3600
}
Save the refresh token to your .env file:
# Add to ~/.env
ZOHO_REFRESH_TOKEN=1000.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The access token expires in 1 hour. The refresh token never expires until you revoke it manually. Your OpenClaw skill will use the refresh token to generate fresh access tokens on the fly.
If you get an INVALID_CODE error, the grant token expired. Go back to Step 2 and generate a new one. Three minutes goes fast.
Step 4: Write the OpenClaw Zoho CRM Skill
OpenClaw skills are Markdown files that tell your agent what a tool does, when to use it, and how to call external APIs. Create the skill directory and file:
mkdir -p ~/.openclaw/workspace/skills/zoho-crm
Create ~/.openclaw/workspace/skills/zoho-crm/SKILL.md with this content:
---
name: zoho-crm
description: Read and write Zoho CRM data. Manage leads, contacts, deals, tasks, and notes via the Zoho CRM REST API.
tools:
- shell
---
# Zoho CRM Skill
## Authentication
Use the environment variable ZOHO_REFRESH_TOKEN to obtain fresh access tokens.
All requests go to https://www.zohoapis.com (adjust for your data center).
Before making any CRM API call, obtain a fresh access token:
curl -s -X POST "https://accounts.zoho.com/oauth/v2/token" \
-d "grant_type=refresh_token" \
-d "client_id=$ZOHO_CLIENT_ID" \
-d "client_secret=$ZOHO_CLIENT_SECRET" \
-d "refresh_token=$ZOHO_REFRESH_TOKEN" \
| jq -r '.access_token'
Store the result as ACCESS_TOKEN for subsequent requests in this session.
## Available Operations
### Search for a Contact or Lead
Search by email, name, or any field:
curl -s -H "Authorization: Zoho-oauthtoken $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-X GET "https://www.zohoapis.com/crm/v6/Contacts/search?email=USER_EMAIL_HERE" \
| jq '.data[0]'
For leads, replace Contacts with Leads in the URL.
### List Deals in Pipeline
Fetch open deals with stage, amount, and close date:
curl -s -H "Authorization: Zoho-oauthtoken $ACCESS_TOKEN" \
"https://www.zohoapis.com/crm/v6/Deals?fields=Deal_Name,Stage,Amount,Closing_Date,Contact_Name&per_page=50" \
| jq '.data[] | {name: .Deal_Name, stage: .Stage, amount: .Amount, close: .Closing_Date}'
### Create a New Lead
curl -s -H "Authorization: Zoho-oauthtoken $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-X POST "https://www.zohoapis.com/crm/v6/Leads" \
-d '{
"data": [{
"Last_Name": "LAST_NAME",
"First_Name": "FIRST_NAME",
"Email": "EMAIL",
"Company": "COMPANY_NAME",
"Phone": "PHONE",
"Lead_Source": "Website"
}]
}' | jq '.data[0]'
### Update a Deal Stage
curl -s -H "Authorization: Zoho-oauthtoken $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-X PUT "https://www.zohoapis.com/crm/v6/Deals/DEAL_ID" \
-d '{
"data": [{
"Stage": "NEW_STAGE_NAME"
}]
}' | jq '.data[0]'
### Add a Note to a Record
curl -s -H "Authorization: Zoho-oauthtoken $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-X POST "https://www.zohoapis.com/crm/v6/Notes" \
-d '{
"data": [{
"Note_Title": "TITLE",
"Note_Content": "CONTENT",
"Parent_Id": "RECORD_ID",
"se_module": "Contacts"
}]
}' | jq '.data[0]'
### Create a Follow-Up Task
curl -s -H "Authorization: Zoho-oauthtoken $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-X POST "https://www.zohoapis.com/crm/v6/Tasks" \
-d '{
"data": [{
"Subject": "TASK_SUBJECT",
"Due_Date": "YYYY-MM-DD",
"What_Id": "DEAL_ID",
"se_module": "Deals",
"Status": "Not Started",
"Priority": "High"
}]
}' | jq '.data[0]'
## Rules
- Always refresh the access token before making API calls. Tokens expire after 1 hour.
- Confirm with the user before creating or updating any record. Show the proposed data and ask for approval.
- When searching, try email first. Fall back to name if no email is provided.
- For deal stage updates, show the current stage and the proposed new stage before executing.
- Rate limit: Zoho CRM Free tier allows 5,000 API credits/day. Standard allows 100,000. If you receive a 429 response, wait 60 seconds and retry once.
- Never log the Client Secret, Refresh Token, or Access Token in responses or memory files.
- Use Zoho-oauthtoken (not Bearer) as the authorization header prefix. This is a Zoho-specific requirement.
Why the Skill File Is Structured This Way
The authentication section teaches the agent to refresh tokens before each session. Unlike HubSpot’s single persistent token, Zoho’s access tokens expire hourly. Putting the refresh command first means the agent handles expiration without you intervening.
The operations give the agent templated curl commands it adapts at runtime. When you ask “find the contact for jane@acme.com,” the agent reads the search pattern, substitutes the email, runs the command, and parses the JSON.
The rules section is the safety net. The confirmation rule prevents your agent from creating duplicate leads or accidentally moving deals to the wrong stage. This can easily happen when natural language instructions are ambiguous, like “update the Acme deal” when there are three Acme deals in the pipeline.
One Zoho-specific detail worth noting: the authorization header format is Zoho-oauthtoken, not the standard Bearer prefix. Miss this and every API call returns a 401. It is in Zoho’s docs but easy to overlook.
Step 5: Test the Connection
Restart OpenClaw so it picks up the new skill:
openclaw gateway restart
Open your Telegram chat (or whichever channel you use) and try these queries:
Token refresh test:
“Can you connect to Zoho CRM and check if the API is working?”
The agent should refresh the access token and make a simple API call. If it fails with INVALID_TOKEN or AUTHENTICATION_FAILURE, check that your .env file has the correct ZOHO_REFRESH_TOKEN.
Search test:
“Find the contact record for jane@example.com in Zoho”
Deal pipeline test:
“Show me all deals closing this month in Zoho CRM”
Write test (start small):
“Create a new lead in Zoho: John Test, john.test@example.com, company Test Corp”
The agent should ask you to confirm before creating the record. If it does not, strengthen the confirmation rule in your SKILL.md.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
INVALID_TOKEN | Access token expired and refresh failed | Check ZOHO_REFRESH_TOKEN in .env; regenerate from Step 3 if revoked |
INVALID_URL_PATTERN | Wrong data center domain | Match your API URL to your account region (see table in prerequisites) |
OAUTH_SCOPE_MISMATCH | Token lacks required scope | Re-do Steps 2-3 with correct scopes |
AUTHENTICATION_FAILURE | Wrong authorization header format | Use Zoho-oauthtoken prefix, not Bearer |
INVALID_DATA | Missing required fields | Zoho requires Last_Name for leads and Deal_Name for deals at minimum |
| Empty results | Wrong module or field name | Zoho uses Last_Name not lastname, Deal_Name not dealname |
| 429 Too Many Requests | Rate limit exceeded | Wait 60 seconds; check your edition’s daily credit limit |
What to Automate First
After the connection works, resist the urge to automate everything immediately. The same phased approach we recommend for HubSpot integrations works here.
Week 1: Read-Only Lookups
Use your agent for quick CRM questions during your workday:
- “What stage is the Acme deal at?”
- “When did we last talk to Sarah at Globex?”
- “Show me all leads from this week”
This builds familiarity with how the agent interprets Zoho’s field names and module structure. Zoho uses title case (Deal_Name, Lead_Source, Closing_Date) which can trip up an agent expecting lowercase.
Week 2: Add Write Operations
Start creating and updating records through the agent:
- “Log a note on the Globex contact: discussed renewal pricing, wants proposal by Friday”
- “Move the Initech deal to Proposal/Price Quote”
- “Create a follow-up task for Dave at Initech, due next Tuesday”
Zoho’s deal stages are customizable per pipeline, so your agent needs to learn your specific stage names. After a few corrections, it will remember them in context.
Week 3: Automated Alerts with Heartbeat
Add periodic CRM checks to your OpenClaw heartbeat. For details on heartbeat configuration, see our OpenClaw heartbeat scheduling guide.
Add this to your heartbeat.md:
## Stale Deal Check
Check Zoho CRM for deals that have been in the same stage for more than 14 days.
If any are found, send me a summary with deal name, current stage, days stuck,
and deal owner. Use the Zoho CRM skill to query deals.
Or a morning pipeline brief:
## Daily CRM Brief (runs at 8:00 AM)
Pull all Zoho CRM deals closing in the next 7 days.
For each deal, check if there is a note or task created in the last 3 days.
Send me a list of deals that need attention, sorted by close date.
This read-then-write-then-automate progression catches mistakes early when they are cheap to fix.
Security Considerations
Zoho’s OAuth 2.0 flow is more secure than a single static API key, but that security only works if you handle the credentials properly.
Scope discipline. Only request the modules you need. If you are not managing tasks or notes yet, leave those scopes out. You can always re-run Steps 2-3 with broader scopes later.
Credential storage. Keep ZOHO_CLIENT_ID, ZOHO_CLIENT_SECRET, and ZOHO_REFRESH_TOKEN in your .env file. Never put them in agents.md, soul.md, or any workspace file your agent reads into context.
Confirmation rules. The skill’s Rules section requires the agent to confirm before any write operation. This is your guard against misunderstood instructions. When you say “update the deal” and there are three deals matching, the agent should ask which one.
Token rotation. If you suspect your refresh token is compromised, revoke it in Zoho API Console under your self-client’s settings and repeat Steps 2-3 to generate new tokens.
For a deeper dive on OpenClaw security, see Step 9 in our OpenClaw setup guide.
Frequently Asked Questions
Do I need a paid Zoho CRM plan to connect OpenClaw?
No. Zoho CRM’s Free edition includes REST API access with 5,000 API credits per day. That covers several hundred CRM operations daily, which is more than enough for conversational use. Paid plans (Standard at $14/user/month and above) unlock higher API limits and features like workflow rules and custom modules, but the core integration works on the free tier.
How long does this setup take?
About 25 minutes if OpenClaw is already running. Registering the self-client takes 5 minutes, generating and exchanging tokens takes another 5, and writing plus testing the skill file takes 15. If you need to install OpenClaw first, add 30-45 minutes for the initial setup.
Can OpenClaw automatically update Zoho CRM records without me asking?
Yes, through heartbeat instructions or scheduled tasks. You write a rule like “every morning, check for deals closing this week with no recent notes,” and the agent runs it on schedule. We recommend starting with read-only reports and adding write permissions only after you trust the agent’s accuracy with your specific CRM data.
What happens if the Zoho API rate limit is hit?
The API returns HTTP 429. For the Free edition (5,000 credits/day), aggressive automation could hit this. Standard edition gives you 100,000 credits/day, which is hard to exhaust through normal use. The skill file includes a retry rule: wait 60 seconds and try once more. If you consistently hit limits, upgrade your Zoho plan or reduce polling frequency in your heartbeat schedules.
Is it safe to give my AI agent CRM access?
As safe as your configuration makes it. Scope your OAuth tokens to only the modules you need, store all credentials in .env files (never in workspace files), and require confirmation for write operations. Your data flows directly between your machine and Zoho’s servers. The biggest risk is prompt injection via content your agent reads, like an email body containing malicious instructions. Current-generation models (GPT-5.4, Claude Opus 4.6) handle this better than older models, but the confirmation rule is your primary defense.
How do I refresh an expired Zoho access token?
The skill file handles this automatically. Before each API session, the agent runs the refresh token endpoint to get a fresh access token. If the refresh token itself stops working (which only happens if you manually revoke it), re-do Steps 2-3 to generate new tokens. Access tokens last 1 hour. Refresh tokens last indefinitely.
Can I use this from WhatsApp instead of Telegram?
Yes. OpenClaw supports WhatsApp, Slack, Discord, and iMessage in addition to Telegram. The Zoho CRM skill works identically regardless of which messaging channel you use. For 24/7 availability across all channels, consider running OpenClaw on a VPS like Hostinger.
Which Zoho data center domain should I use?
Check your browser URL when logged into Zoho CRM. If it shows crm.zoho.com, use accounts.zoho.com for OAuth and www.zohoapis.com for API calls. If it shows crm.zoho.eu, use the .eu variants. Using the wrong domain returns authentication errors even with valid credentials.
Key Takeaways
- Connect Zoho CRM to OpenClaw by registering a self-client in Zoho’s API Console, exchanging a grant token for OAuth credentials, and writing a custom SKILL.md file
- Use the self-client OAuth flow, not a redirect-based flow, since no web server is needed for backend agent integrations
- Store all three credentials (Client ID, Client Secret, Refresh Token) in
.envfiles, never in workspace files the agent reads - The authorization header format is
Zoho-oauthtoken, notBearer. Getting this wrong causes every API call to fail silently with a 401 - Start with read-only lookups for the first week before enabling write operations, then add heartbeat-based automation in week three
- Zoho CRM Free edition supports the full API integration with 5,000 credits/day and no paid connectors required
SFAI Labs