
Complete in Under 60 Minutes
Real Deluge Code Included
Leave + Expense + Asset Approvals
India HR Compliance Context
Every Indian HR team has the same problem: an employee submits a leave request in Zoho People at 9 AM, the manager checks Zoho People at 3 PM, approves it at 3:05 PM, and the employee has been WhatsApping colleagues all day asking “approval aaya kya?” This 6-hour delay — for a 30-second decision — exists because approvals live in Zoho People but managers live in Zoho Cliq. This guide bridges that gap completely. By connecting Zoho People and Zoho Cliq automation, your managers will receive a Cliq message with the full leave request and two buttons — Approve and Reject — the moment an employee submits. The manager clicks one button in Cliq. Zoho People updates automatically. The employee gets notified. Total decision time: under 60 seconds. Total build time: under 60 minutes. This guide covers the complete setup — from Cliq webhook to Deluge function to interactive bot buttons — with real, copy-ready code for leave approvals, expense approvals, and asset requests.

Employee submits leave/expense/asset request in Zoho People
Zoho People Workflow Rule triggers a Deluge Custom Function
Deluge calls Zoho Cliq webhook — posts interactive message card to manager’s channel
Manager sees Approve / Reject buttons in Cliq — clicks one button
Cliq Bot handler receives click event — calls Zoho People API to update status
Employee receives Cliq DM notification — “Your leave request has been approved by [Manager Name]”
- Prerequisites and Plan Requirements
- Step 1 — Create Cliq Incoming Webhook (5 min)
- Step 2 — Write the Deluge Custom Function (15 min)
- Step 3 — Configure Zoho People Workflow Rule (10 min)
- Step 4 — Build the Cliq Bot Action Handler (20 min)
- Bonus — Expense Approval Automation
- India HR Context — Leave Types and Compliance
- Troubleshooting Common Issues
- 5 Enhancements to Build Next
- FAQs — Zoho People Cliq Automation
Prerequisites and Plan Requirements
- Zoho People Professional or above (for Custom Functions)
- Zoho Cliq (any plan — free tier works)
- Both products connected to the same Zoho Organisation
- Admin access to both Zoho People and Zoho Cliq settings
- Leave approval notification with Approve + Reject buttons in Cliq
- One-click approval that auto-updates Zoho People status
- Employee DM notification on approval or rejection
- Manager escalation if no response in 24 hours
- Audit trail — every approval decision logged in People
| Zoho People Plan | Custom Functions | Workflow Rules | API Access | This Guide Works? |
|---|---|---|---|---|
| Essential HR | No | Limited | Yes | No |
| Professional | Yes | Yes | Yes | Yes |
| Premium | Yes | Yes | Yes | Yes |
| Enterprise | Yes | Yes | Yes | Yes |
Step 1 — Create the Zoho Cliq Incoming Webhook 5 minutes
The incoming webhook is Cliq’s endpoint that receives messages from external systems — in this case, from Zoho People’s Deluge function. Every time a leave request is submitted, Deluge will POST a JSON payload to this URL and Cliq displays it as a message.
- Open Zoho Cliq → navigate to the channel where approvals should appear (create a new channel called
#hr-approvalsif needed) - Click the channel name at the top → select Settings
- Go to Bots and Integrations → click Incoming Webhooks
- Click Add Incoming Webhook — name it
HR Approvals Bot - Click Generate Webhook URL — copy and save this URL securely
- The URL format will be:
https://cliq.zoho.in/api/v2/channelsbyname/hr-approvals/message?zapikey=YOUR_KEY
Reject
Step 2 — Write the Deluge Custom Function in Zoho People 15 minutes
Zoho People → Settings → Automation → Custom Functions → Add Custom Function
Module: Leave | Trigger: On Record Add (when a new leave request is submitted)
Leave Approval Cliq Notification — Complete Deluge Function
// Zoho People Custom Function — Leave Approval to Cliq
// Trigger: Leave module, On Record Add
// Module: Leave Tracker
void sendLeaveApprovalToCliq(string leaveId)
{
// ================================================
// STEP 1: Fetch the leave request details
// ================================================
leaveRecord = zoho.people.getRecordById("P_LeaveTracker", leaveId);
employeeName = leaveRecord.get("Employee_Name").toString();
employeeId = leaveRecord.get("Employee_ID").toString();
leaveType = leaveRecord.get("Leave_Type").toString();
fromDate = leaveRecord.get("From").toString();
toDate = leaveRecord.get("To").toString();
noOfDays = leaveRecord.get("No_of_Days").toString();
reason = leaveRecord.get("Reason").toString();
// Get reporting manager email
empRecord = zoho.people.searchRecords("P_Employee",
"Employee_ID", "=", employeeId);
managerEmail = empRecord.get(0).get("Reporting_To_MailID").toString();
managerName = empRecord.get(0).get("Reporting_To").toString();
// ================================================
// STEP 2: Fetch employee leave balance
// ================================================
balanceResp = zoho.people.getLeaveBalance(employeeId, leaveType);
leaveBalance = balanceResp.get("Balance").toString();
// ================================================
// STEP 3: Build the Cliq message card with buttons
// Interactive message format using Cliq's card schema
// ================================================
// Encode leave ID and employee ID in button actions
approveAction = "APPROVE|" + leaveId + "|" + employeeId;
rejectAction = "REJECT|" + leaveId + "|" + employeeId;
// Build Cliq interactive message payload
cliqPayload = Map();
cliqPayload.put("text", "Leave Request — Action Required");
// Message card object
card = Map();
card.put("title", "Leave Approval Request");
card.put("theme", "modern-inline");
// Slide content (body of the card)
slide = Map();
slide.put("type", "label");
dataRows = list();
dataRows.add(Map({"label":"Employee","value": employeeName + " (" + employeeId + ")"}));
dataRows.add(Map({"label":"Leave Type","value": leaveType}));
dataRows.add(Map({"label":"From","value": fromDate}));
dataRows.add(Map({"label":"To","value": toDate}));
dataRows.add(Map({"label":"Duration","value": noOfDays + " day(s)"}));
dataRows.add(Map({"label":"Reason","value": reason}));
dataRows.add(Map({"label":"Balance After Approval","value": leaveBalance + " days remaining"}));
slide.put("data", dataRows);
slides = list();
slides.add(slide);
card.put("slides", slides);
// Action buttons — Approve (green) and Reject (red)
buttons = list();
approveBtn = Map();
approveBtn.put("label", "Approve");
approveBtn.put("hint", "Approve this leave request");
approveBtn.put("action", Map({
"type": "invoke.function",
"name": "handleLeaveAction",
"data": Map({"action": approveAction, "manager": managerName})
}));
buttons.add(approveBtn);
rejectBtn = Map();
rejectBtn.put("label", "Reject");
rejectBtn.put("hint", "Reject this leave request");
rejectBtn.put("action", Map({
"type": "invoke.function",
"name": "handleLeaveAction",
"data": Map({"action": rejectAction, "manager": managerName})
}));
buttons.add(rejectBtn);
card.put("buttons", buttons);
cliqPayload.put("card", card);
// ================================================
// STEP 4: Post to Cliq via incoming webhook
// ================================================
cliqWebhookUrl = "https://cliq.zoho.in/api/v2/channelsbyname/hr-approvals/message?zapikey=YOUR_ZAPIKEY";
webhookResp = invokeurl
[
url : cliqWebhookUrl
type : POST
headers : {"Content-Type": "application/json"}
body : cliqPayload.toString()
];
info "Cliq notification sent for leave: " + leaveId
+ " | Response: " + webhookResp;
// ================================================
// STEP 5: Also send a DM to the specific manager
// (in case they are not monitoring the channel)
// ================================================
dmPayload = Map();
dmPayload.put("text", "You have a pending leave approval from "
+ employeeName + " for " + noOfDays
+ " day(s) from " + fromDate
+ ". Check #hr-approvals channel to approve.");
dmUrl = "https://cliq.zoho.in/api/v2/chat?zapikey=YOUR_ZAPIKEY"
+ "&channel=" + managerEmail;
dmResp = invokeurl
[
url : dmUrl
type : POST
headers : {"Content-Type": "application/json"}
body : dmPayload.toString()
];
info "DM sent to manager: " + managerEmail;
}
Step 3 — Configure the Zoho People Workflow Rule 10 minutes
The workflow rule is the trigger that calls your custom function automatically when a leave is submitted. Without this, the Deluge function only runs when manually called.
- Go to Zoho People → Settings → Automation → Workflow
- Click Add Workflow
- Name:
Leave Approval — Cliq Notification - Module: Leave
- Trigger: On Record Add (when an employee submits leave)
- Conditions (optional): if you want only specific leave types to trigger, add condition:
Leave Type is Earned Leave OR Casual Leave OR Sick Leave - Under Actions, click Add Action → select Custom Function
- Select your function:
sendLeaveApprovalToCliq - Map the parameter:
leaveId→${Leave ID}(the leave record’s ID field) - Click Save
Step 4 — Build the Cliq Bot Action Handler 20 minutes
The incoming webhook posts the message and buttons to Cliq — but when a manager clicks Approve or Reject, Cliq needs to know what to do with that click. That is the bot’s action handler — a Cliq-side Deluge function that receives the button click, calls the Zoho People API to update the leave status, and posts a confirmation message.
- In Zoho Cliq → Admin Panel → Bots → Create Bot
- Name:
HR Approvals Bot| Description: “Handles leave and expense approvals” - Under Handlers, click Add Handler → select Function
- Name the handler:
handleLeaveAction(must match the name in your button action above) - Write the Deluge handler code below:
Cliq Bot Handler — Processes Approve and Reject Clicks
// Zoho Cliq Bot Handler: handleLeaveAction
// Triggered when manager clicks Approve or Reject button
// in the HR approval Cliq message
Map handleLeaveAction(Map payload)
{
// Extract the action data from button click
actionData = payload.get("data");
actionString = actionData.get("action").toString(); // "APPROVE|leaveId|empId"
managerName = actionData.get("manager").toString();
managerEmail = payload.get("user").get("email").toString();
// Parse action string
parts = actionString.split("|");
decision = parts.get(0); // "APPROVE" or "REJECT"
leaveId = parts.get(1);
employeeId = parts.get(2);
// ================================================
// Call Zoho People API to update leave status
// ================================================
newStatus = (decision == "APPROVE") ? "Approved" : "Rejected";
// Zoho People API: Update Leave Status
updatePayload = Map();
updatePayload.put("recordId", leaveId);
updatePayload.put("status", newStatus);
updatePayload.put("remarks", "Decision taken by " + managerName
+ " via Zoho Cliq on "
+ today().toString("dd-MMM-yyyy"));
// Call Zoho People REST API with OAuth token
peopleApiUrl = "https://people.zoho.in/api/forms/P_LeaveTracker/"
+ "updateRecord?recordId=" + leaveId;
apiResp = invokeurl
[
url : peopleApiUrl
type : POST
headers : {"Authorization": "Zoho-oauthtoken " + zoho.oauthtoken,
"Content-Type": "application/json"}
body : updatePayload.toString()
];
// ================================================
// Fetch employee Cliq handle to send them a DM
// ================================================
empRecord = zoho.people.searchRecords("P_Employee",
"Employee_ID", "=", employeeId);
employeeName = empRecord.get(0).get("First_Name").toString();
employeeEmail= empRecord.get(0).get("EmailID").toString();
// Send DM to employee with decision
dmText = (decision == "APPROVE")
? "Your leave request has been *approved* by " + managerName + ". Enjoy your leave!"
: "Your leave request has been *rejected* by " + managerName
+ ". Please reach out to your manager for more details.";
dmPayload = Map();
dmPayload.put("text", dmText);
dmUrl = "https://cliq.zoho.in/api/v2/chat?zapikey=YOUR_ZAPIKEY"
+ "&channel=" + employeeEmail;
invokeurl
[
url : dmUrl
type : POST
headers : {"Content-Type": "application/json"}
body : dmPayload.toString()
];
// ================================================
// Return response to update the Cliq message
// (replaces the Approve/Reject buttons with a status)
// ================================================
statusIcon = (decision == "APPROVE") ? "tick-circle" : "times-circle";
statusColor= (decision == "APPROVE") ? "#2e7d32" : "#c62828";
statusText = (decision == "APPROVE")
? "Approved by " + managerName
: "Rejected by " + managerName;
responseMsg = Map();
responseMsg.put("text", "*Leave Request " + newStatus + "*\n"
+ employeeName + "'s leave has been "
+ newStatus.toLowerCase() + " by " + managerName
+ " on " + today().toString("dd-MMM-yyyy"));
return responseMsg;
}
zoho.oauthtoken variable in Cliq bot functions automatically provides the token for the bot owner’s account. Ensure the bot is owned by a Zoho People admin account that has permission to approve leaves. If the bot owner does not have approval permissions, the API call will fail with a 403 error.Bonus — Expense Approval Automation with Zoho People + Cliq
The same architecture works for expense approvals. Create a second custom function triggered when an expense is submitted in Zoho People’s expense module:
// Zoho People Custom Function — Expense Approval to Cliq
// Trigger: Expense module, On Record Add
// Adapted from leave approval function
void sendExpenseApprovalToCliq(string expenseId)
{
// Fetch expense record
expRecord = zoho.people.getRecordById("P_Expense", expenseId);
employeeName = expRecord.get("Employee_Name").toString();
employeeId = expRecord.get("Employee_ID").toString();
expenseDate = expRecord.get("Expense_Date").toString();
category = expRecord.get("Category").toString();
amount = expRecord.get("Amount").toString();
currency = expRecord.get("Currency").toString();
description = expRecord.get("Description").toString();
receiptUrl = expRecord.get("Receipt_Attachment").toString();
// Get manager details
empRecord = zoho.people.searchRecords("P_Employee",
"Employee_ID", "=", employeeId);
managerEmail = empRecord.get(0).get("Reporting_To_MailID").toString();
managerName = empRecord.get(0).get("Reporting_To").toString();
// Build expense approval message
approveAction = "EXP_APPROVE|" + expenseId + "|" + employeeId;
rejectAction = "EXP_REJECT|" + expenseId + "|" + employeeId;
cliqPayload = Map();
cliqPayload.put("text", "Expense Claim — Approval Required");
card = Map();
card.put("title", "Expense Approval Request");
card.put("theme", "modern-inline");
slide = Map();
slide.put("type", "label");
dataRows = list();
dataRows.add(Map({"label":"Employee","value": employeeName}));
dataRows.add(Map({"label":"Date","value": expenseDate}));
dataRows.add(Map({"label":"Category","value": category}));
dataRows.add(Map({"label":"Amount","value": currency + " " + amount}));
dataRows.add(Map({"label":"Description","value": description}));
if(receiptUrl != "" && receiptUrl != null)
{
dataRows.add(Map({"label":"Receipt","value": "[View Receipt](" + receiptUrl + ")"}));
}
slide.put("data", dataRows);
slides = list();
slides.add(slide);
card.put("slides", slides);
// Buttons
buttons = list();
approveBtn = Map({"label":"Approve","action":Map({
"type":"invoke.function","name":"handleExpenseAction",
"data":Map({"action":approveAction,"manager":managerName})})});
rejectBtn = Map({"label":"Reject","action":Map({
"type":"invoke.function","name":"handleExpenseAction",
"data":Map({"action":rejectAction,"manager":managerName})})});
buttons.add(approveBtn);
buttons.add(rejectBtn);
card.put("buttons", buttons);
cliqPayload.put("card", card);
// Post to #hr-approvals channel
cliqWebhookUrl = "https://cliq.zoho.in/api/v2/channelsbyname/"
+ "hr-approvals/message?zapikey=YOUR_ZAPIKEY";
invokeurl
[
url : cliqWebhookUrl
type : POST
headers : {"Content-Type": "application/json"}
body : cliqPayload.toString()
];
info "Expense approval sent to Cliq for: " + expenseId;
}
India HR Context — Leave Types and Compliance Considerations
India’s labour laws create specific leave approval requirements that affect how you configure the Zoho People Cliq automation. Understanding these helps you build the right approval rules — and what information to include in the Cliq message.
| Leave Type | Statutory Basis | Typical Entitlement | Approval Consideration in Cliq Message |
|---|---|---|---|
| Earned Leave (EL) | Factories Act / Shops Act | 15-21 days/year | Show accrued balance and whether adequate notice was given (typically 3-7 days) |
| Casual Leave (CL) | State Shops Act | 8-12 days/year | Cannot carry forward — show days remaining in current year |
| Sick Leave (SL) | State Shops Act | 6-10 days/year | Auto-approve below 3 days — only flag for manager if 3+ days (medical certificate required) |
| Maternity Leave | Maternity Benefit Act 1961 | 26 weeks (paid) | Auto-approve — statutory right. Cliq message should go to HR head, not just line manager |
| Paternity Leave | Company policy (no statute for private sector) | As per policy | Show company policy entitlement in Cliq message for manager reference |
Auto-Approve Short Sick Leave — Deluge Code
// Add this condition BEFORE calling the Cliq notification
// in your sendLeaveApprovalToCliq function
void sendLeaveApprovalToCliq(string leaveId)
{
leaveRecord = zoho.people.getRecordById("P_LeaveTracker", leaveId);
leaveType = leaveRecord.get("Leave_Type").toString();
noOfDays = leaveRecord.get("No_of_Days").toDecimal();
// Auto-approve Sick Leave of 2 days or less
if(leaveType == "Sick Leave" && noOfDays <= 2)
{
autoApprovePayload = Map();
autoApprovePayload.put("recordId", leaveId);
autoApprovePayload.put("status", "Approved");
autoApprovePayload.put("remarks", "Auto-approved by system: Sick leave up to 2 days.");
invokeurl
[
url : "https://people.zoho.in/api/forms/P_LeaveTracker/updateRecord?recordId=" + leaveId
type : POST
headers: {"Authorization": "Zoho-oauthtoken " + zoho.oauthtoken,
"Content-Type": "application/json"}
body : autoApprovePayload.toString()
];
info "Sick leave auto-approved: " + leaveId;
return; // Exit function — no Cliq notification needed
}
// For all other leave types, continue to Cliq notification
// ... (rest of the function from Step 2)
}
Troubleshooting Common Issues
Check 1: Go to Zoho People → Settings → Automation → Workflow → click your workflow → View Execution Logs. If the log shows an error, the Deluge function has a problem. Check 2: Verify your Cliq webhook URL is correct — copy it fresh from Cliq and paste it again. The zapikey expires if regenerated. Check 3: Ensure the Zoho People custom function is saved and the workflow rule is in Active status (not Draft).
The most common cause is the bot owner not having Zoho People admin permissions. The zoho.oauthtoken in the Cliq bot handler uses the bot owner’s identity for API calls. Ensure the bot is created by (or transferred to) a Zoho People admin account. Alternatively, use a dedicated service account with People admin access as the bot owner.
The DM API uses the employee’s email address as the channel identifier. If the email stored in Zoho People does not exactly match the email registered in Zoho Cliq for that user, the DM fails silently. Verify by checking empRecord.get(0).get("EmailID") against the employee’s actual Cliq login email. For organisations where People email and Cliq email differ (rare but possible), use the Cliq user’s Cliq handle instead of email.
The bot handler function name must match exactly between the button action definition ("name":"handleLeaveAction") and the bot’s registered handler name in Cliq Admin Panel. Both must be handleLeaveAction — case sensitive. Verify the bot is published (not in draft) and the handler is Active.
5 Enhancements to Build Next
A scheduled Deluge function runs daily, checks all pending leave requests older than 24 hours, and sends a reminder to the manager’s Cliq DM: “You have 3 pending approvals. Oldest request is 26 hours old.” Prevents approvals from being forgotten during busy periods.
Instead of a simple Reject button, show a “Reject with Reason” dialog in Cliq where the manager types the rejection reason before submitting. The reason is stored in Zoho People’s remarks field and included in the employee’s rejection DM, reducing “why was my leave rejected?” follow-up calls.
Before posting the Cliq notification, the Deluge function queries Zoho People’s leave records to check how many team members are already on leave during the requested dates. If more than 30% of the team is already on leave, the Cliq message includes a warning: “Note: 4 team members are already on leave during this period.”
Extend the same architecture to asset requests — laptop, phone, access card, software licence. The Cliq message shows the asset requested, cost, business justification, and routes to the department head for approval. Add a Zoho Books purchase order creation step that fires automatically on approval for purchases above ₹5,000.
Every Monday morning, a scheduled function posts a summary card to the #hr-approvals channel listing all pending approvals by manager: “Rajesh Kumar: 2 pending | Neha Singh: 1 pending | Amit Sharma: 0 pending.” HR managers can spot bottlenecks before they become employee complaints.
Need Help Setting Up Zoho People + Cliq Automation?
Codroid Labs builds complete Zoho People + Cliq automation systems for Indian businesses — leave approvals, expense approvals, asset requests, onboarding workflows, and attendance integrations. Fixed INR pricing. Hindi support. 90-day warranty.
Free 60-minute consultation. Pan India. Remote implementation. Working system delivered in 1 week.
Zoho People + Cliq Automation — 12 Questions Answered
Does Zoho People have native Zoho Cliq integration?
Yes. Zoho People and Zoho Cliq share the same organisational user base when connected to the same Zoho account. Zoho People’s Deluge custom functions include the built-in zoho.cliq.postToChannel function for basic channel messages. For interactive approval buttons, you need either an incoming webhook (for the notification) combined with a Cliq bot action handler (for button responses), as shown in this guide. The Zoho People Cliq automation described here does not require any third-party middleware — everything runs within the Zoho ecosystem.
Which Zoho People plan is needed for this automation?
The Zoho People Professional plan or above is required. Essential HR does not include custom Deluge functions. Professional, Premium, and Enterprise all support custom functions and workflow rules needed for this guide. Zoho One subscribers get both Zoho People and Zoho Cliq included — no additional licencing required. The Cliq bot feature is available on all Cliq plans including the free tier.
Can a manager approve from Cliq mobile app?
Yes — this is one of the most valuable features of the Zoho People Cliq automation setup. The Cliq mobile app (Android and iOS) displays interactive message cards with buttons, exactly as they appear on desktop. A manager on the road can receive a leave request push notification, open Cliq on their phone, tap Approve, and the leave is updated in Zoho People — all without opening a browser or logging into Zoho People directly.
How do I handle multi-level approvals (manager + HR head)?
Build a two-stage workflow: Stage 1 sends the Cliq approval message to the line manager. When the manager approves (Stage 1 complete), the bot handler does not immediately mark the leave as “Approved” in Zoho People — instead, it changes the status to “Pending HR Approval” and sends a second Cliq notification to the HR head with the manager’s approval noted. Only when the HR head clicks Approve does the final “Approved” status get written to Zoho People. This two-stage pattern is common in Indian companies for leaves above 5 days or for maternity/paternity leaves.
Official Resources — Zoho People + Cliq Automation
- Zoho People REST API — Leave Management Endpoints
- Zoho Cliq — Incoming Webhooks Developer Documentation
- Zoho Cliq — Bot Development Guide
- Zoho Deluge Language Reference — invokeURL and API Calls
- Zoho Books India GST — Expense Claims to Invoice Integration
- Book Free Consultation — Codroid Labs Zoho People Automation India
