
Howdy Tech Wizards!
Welcome back to a fresh week of Kaizen!
This time, we are diving into a two-part series where you will learn how to build a Timer and Worklog Widget for Zoho CRM. This widget helps track active work time and log multitasking sessions using Zoho CRM
APIs,
Client Script,
Functions, and
Workflows.
Why It Matters?
Zoho CRM effectively captures stage/status transitions, like tracking when a case moves from Open to In Progress or On Hold. However, in dynamic work environments, employees juggle multiple work items and engage in impromptu tasks or conversations.
Let’s take the On Hold status as an example.
While it can be used to indicate pauses in progress, it does not always align with real-world workflows. Consider these scenarios:
Not all interruptions justify a status change
Moving the case to On Hold for every minor detour (to clarify something with a colleague or respond to another emergency work item) would be impractical and could lead to under reporting actual work hours. Over time, this untracked effort adds up, creating a gap in visibility.
Frequent status changes may dilute their meaning
In many teams, On Hold signifies a legitimate blocker like waiting on customer input or an external dependency. Using it frequently to reflect quick shifts in attention could compromise that clarity.
A timer widget solves this by:
- Capturing hands-on work items.
- Logging context switches (unrelated tasks) with descriptive entries.
- Feeding structured entries into a custom module for reporting and subform sync.
Business Scenario: IT Service Desk & Ticket Resolution
A tech support team uses the Cases module to resolve customer issues. Some tickets are straightforward; others require follow-ups, escalations, or cross-team coordination. While CRM timestamps (like stage transitions or picklist tracking) track when changes happen, they do not reflect how long someone actively worked on a case.
This Timer Widget can be the ultimate solution to track down the active time spent on each case through out the day.
What are we Building?
By the end of this two-part guide, you will know how to:
- Build a timer widget to track time per task or any CRM record.
- Log work sessions into a custom module every time the timer is started/stopped.
- Automatically populate a subform in the corresponding record module using workflows and Deluge functions.
- Leverage the Reports module to analyze work patterns, SLA adherence, and productivity trends.
In Part I, we will focus on:
-> Building and configuring the Timer Widget.
-> Capturing each timed session as a record in a custom module.
Prerequisites
1. Create a Custom Module
Create a custom module named Timer Entries to log work details and generate reports from the Reports module. A new record will be added to this module each time a timer is started.
Set up the following custom fields in the Timer Entries module:
| |
Start Time | DateTime |
End Time | DateTime |
Total Duration (in mins.) | Formula DateBetween(${Timer Entries.Start Time},${Timer Entries.End Time},'Minutes') |
Related to Case | Lookup to Cases module |
Work Description | |
2. Add a custom picklist option
Add a custom picklist option called In Progress to the Status picklist field in the Cases module for precisely identifying the status of the cases.
3. Setup Custom Views for Contextual Filtering
To streamline the widget experience and ensure users only see relevant records to associate while tracking time, set up two smart custom views—one in the Cases module and another in the Timer Entries module.
Active Cases View (Cases Module)
Create a custom view in the Cases module to list only the active tickets the logged-in user is working on. Use the following criteria tips:
- Use the Status field to filter records with values like In Progress, On Hold, or Escalated.
- Use the Case Owner field to show records that are assigned only to the currently logged-in user.
This view powers the drop-down inside the widget where users select the case they want to start the timer for.
Active Timers View (Timer Entries Module)
Set up a second custom view in the Timer Entries module to track entries where the timer has been started but not yet stopped. These represent active timers. Use the following logic for criteria:
- The End Time field is empty (i.e., timer still running).
- Timer Entry owner field matches the logged-in user.
This view is used internally by the widget to detect if a timer is already running and update the same entry once the timer is stopped.
Follow the
Managing List View help page and use the specifications shown in the following image.
Building the Timer Widget
Step -1: Review Basics
Refer to our earlier
Kaizen on CLI Installation, Creating a Widget Project, and Internal hosting of the same.
Step 2 - Develop the Widget
After initializing your widget project using CLI, implement the timer logic:
Fetching Active Cases
On page load, the
populateRecordsDropdown function initiates a
Get Records API call to the Cases module, using the Active Cases custom view ID. This fetches all active case records assigned to the logged-in user.
These records are then listed in a dropdown, allowing users to quickly select the relevant case they are about to work on.
async function populateRecordsDropdown() { const recordsDropdown = document.getElementById("moduleRecords"); recordsDropdown.innerHTML = "";
try { const recordsResponse = await ZOHO.CRM.API.getAllRecords({ Entity: casesModule, cvid: casesCVID, per_page: 10 });
if (recordsResponse.data && recordsResponse.data.length > 0) { recordsResponse.data.forEach(record => { const option = document.createElement("option"); option.value = record.id; option.textContent = record.Subject || "Unnamed Record"; recordsDropdown.appendChild(option); }); } else { const placeholderOption = document.createElement("option"); placeholderOption.value = ""; placeholderOption.textContent = "No records found"; placeholderOption.disabled = true; placeholderOption.selected = true; recordsDropdown.appendChild(placeholderOption); } } catch (error) { console.error("Error fetching records:", error); } } |
Starting the Timer and Creating an Entry
Once the user starts the timer, The
createRecord function triggers a
Create Record API call to log the session in the Timer Entries custom module. The record captures the start time and the related case and description (if provided).
Even if no details are entered or case is selected, a timer entry is still created with the start time. This ensures that spontaneous work sessions are tracked and not lost.
async function createRecord(startTime) { try { const workDescription = document.getElementById("workDescription").value; const selectedRecordId = document.getElementById("moduleRecords").value; const selectedRecordText = document.getElementById("moduleRecords").options[ document.getElementById("moduleRecords").selectedIndex ].text;
const data = { Start_Time: startTime, Owner: currentUserId, Work_Description: workDescription, Related_to_Case: selectedRecordId, Name: selectedRecordText };
const response = await ZOHO.CRM.API.insertRecord({ Entity: timerModule, APIData: data });
console.log("Start time recorded successfully"); } catch (error) { console.error("Error creating record:", error); } }
|
Stopping the Timer and Updating the Entry
When the timer is stopped, the widget uses the Active Timer Entry custom view to locate the most recent Timer Entry record created by the logged-in user that does not have an end time.
The
updateRecord is then triggered to update that active entry using the
Update Record API call. It updates the End time of the session and the related case, descriptions, if it was not already provided when the timer was started.
async function updateRecord(endTime) { try { const workDescription = document.getElementById("workDescription").value; const selectedRecordId = document.getElementById("moduleRecords").value;
const response = await ZOHO.CRM.API.getAllRecords({ Entity: timerModule, cvid: timerEntriesCVID, per_page: 1 });
const latestRecord = response.data[0]; if (latestRecord) { const recordId = latestRecord.id; const data = { id: recordId, End_Time: endTime, Work_Description: workDescription, Related_to_Case: selectedRecordId };
await ZOHO.CRM.API.updateRecord({ Entity: timerModule, APIData: data, RecordID: recordId }); console.log("End time updated successfully"); } } catch (error) { console.error("Error updating record:", error); } } |
Step 3 - Validate and Pack the Widget
- Follow the steps given in the Widget help page to validate and package the widget.
- Go to Zoho CRM > Setup > Developer Hub > Widgets and click Create New Widget.
- Fill in the required details as shown in this image and ensure to select Button as the widget type.
Step 4 - Associate it with Flyout
- Go to Setup > Developer Space > Client Script. Click New Script.
- Enter a name and description for the script. Choose Command type in Category.
- Create a Flyout and render a widget within it using its details like the API name of the widget, title, size, and animation type. You can get the Widget API name from the widget's detail page.
let allowedUserEmails = []; allowedUserEmails.push('user1_email_address'); allowedUserEmails.push('user2_email_address'); allowedUserEmails.push('user3_email_address'); let currentUserEmail = $Crm.user.email; if (allowedUserEmails.indexOf(currentUserEmail) == -1) { ZDK.Client.showMessage('Command access resticted', { type: 'error' }); return false; } ZDK.Client.createFlyout('myFlyout', {header: 'Timer', animation_type: 4, height: '600px', width: '500px', top: '10px', left: '10px', bottom: '10px', right: '10px' }); ZDK.Client.getFlyout('myFlyout').open({ api_name: 'Timer', type: 'widget' }); return true; |
Refer to
Creating a Client Script help page for more details.
Try It Out!
A complete working code sample of this widget is attached to this post.
Now, let us see how this Timer widget works:
1. Start the Timer
Open the widget and select an active case from the dropdown. As soon as you start the timer, a new record will be automatically created in the Timer Entries module to capture the session.
2. Stop the Timer
When the task is complete, provide a description of the work you have done and stop the timer. The same Timer Entry record (created when the timer was started) will be updated automatically with the end time and your work description.
This forms the foundation for accurate time tracking at the record level.
In Part 2, we will show how to:
-> Use a workflow and Deluge function to transfer these entries into the Work Log subform inside the relevant Cases record.
-> Use CRM Reports to slice and dice work time for better SLA and productivity insights.
In the meantime, would you like us to address any of your pain-points or have a topic to discuss? Let us know in the comments or reach out to us via
support@zohocrm.com.
Until next time, Happy Coding!
-----------------------------------------------------------------------------------------------------------------
Related Reading
-----------------------------------------------------------------------------------------------------------------
Recent Topics
Popup or Highlight on the Form based on a comparison
I have a field for Engine Odometer, and a field for next oil change in KM. Is it possible to generate a popup, or highlight the form, if the Engine Odometer number is larger than the next oil change in KM number?
Change Last Name to not required in Leads
I would like to upload 500 target companies as leads but I don't yet have contact people for them. Can you enable the option for me to turn this requirement off to need a Second Name? Moderation update (10-Jun-23): As we explore potential solutions for
Remove the dot menu and + sign on sub form
If I don't want the user to be able to add more entries on a subform, am I able to remove the dot menu and the + sign?
to close auto payment system for renew my subscription. and get refund my deduct money.
please consider and refund my money.
Allow Zoho form to send to one of our ogranizations groups
All emails from the form submission are being held for moderation. I have permissions set to organization members, and I think I have the forms setup in our DMARC
Unified customer portal login
As I'm a Zoho One subscriber I can provide my customers with portal access to many of the Zoho apps. However, the customer must have a separate login for each app, which may be difficult for them to manage and frustrating as all they understand is that
Writing Checks to Employees for Reimbursable Expenses
I couldn't find a way to write a check through books or expense to an employee for reimbursable expenses. The expense created an entry in the system with a debit (expense) credit (liability). I entered a bill and used the liability account so it would
Best way to automate quotes in CRM
I am trying to take specific information from prospects/clients through Zoho Forms (contact info, square footage, surface area, etc.) and auto generate quotes based on the information submitted. Ideally, the quote would be sent immediately after the form is submitted. I know Zoho has a few different ways to achieve this and wanted to know if there was a 'best practice' for automating the quotation function within CRM. Or if there was another app that can perform this functionality better (Zoho Commerce,
Disable fields in multiple subform rows
Hello everybody! I have an odd one here. I have a subform that collects hours of operation. It contains these fields: Days, Type, Open, and Closed. On load of the form I add 7 rows with Mon - Sun in the Days field. I then disable that field and the add/delete
Sync Creator form submissions to WorkDrive folder
I've made 10 Creator applications, and need to sync my each application's submissions into a WorkDrive folder. I need the form submissions to be PDF file type and sync to a specific folder for documentation purposes. I have tried to use a workflow, but
Share passwords/secrets and folders/chambers with external users
Currently you allow sharing passwords with internal users, internal user groups, and third parties. The third party feature gives temporary access of 30 minutes and it does not have any sign up. What we really need is to properly share secrets/passwords (and ideally folders/chambers) with Zoho Vault users that are not part of our organisation - even if they are on the free plan. If they accept the share, the password would be stored in their Zoho Vault as long as I maintain the share. If I revoke
View Kanban tasks in "Status" layout for all projects
Hi I'm testing Zoho Projects Express to see if it is suitable for my business. So far it looks great and seems to do everything we want (except critical path on the Gantt charts), but one thing I can't seem to figure out is this: If I go into a project, and choose "Kanban", I can select the "Status" layout which is great. I can see the status of all of the tasks in that project, and who is working on what. However, if I go to: Home > My Tasks > Kanban, then the "Status" layout isn't an option - only
Subform dynamic fields on Edit, Load of Main form.
Main Form: Time_Entry Sub Form (separate form): Time_Entries Time_Entries.Time_Entry_No is lookup to - Time_Entry.Time_Sheet_ID (auto number). I would like to disable some of the subform fields upon load (when edited) of the Time_Entry main form. What
Save Draft in email bigin for desktop and mobile
Hi any news to when we going to have the save draft for email in bigin desktop and mobile?
Zoho Desk and Zoho Inventory
I am hoping I am not the only one with this need but has anyone else notice the lack of integration between Zoho Desk and Zoho Inventory and eventual funneling into an Invoice in Zoho Books? As an IT service provider we very often will sell parts (items) along with services for installing said item(s). I have discovered that although you can integrate your Inventory Items into Desk as a "Product", it serves no real functionality. In fact, I found the concept confusing compared to how many Service
Including Field in email body based on answer
I am making a form as a checklist of our mechanics. I have it setup with choice matrix. The choices are "Bad" and "Good". I would like to have only the "Bad" show up in the email body. I am not against changing how it is setup, thank you.
Remove attachment from ticket
Hello, When we receive e-mails from our customers, lots of those e-mails contain attachments with sensitive information, which we need to delete from the ticket after using it. It is forbidden for our company to store these attachments, due to security reasons. Is there a possibility to delete an attachment from a ticket in any way? It is necessary for us that this possibility is available. Thanks in advance, Yorick
Fetch function not working for CRM Contacts
I've created a flow that checks if a contact exists in CRM (based on form input), and if it does, then it updates one of the fields for the contact. In my test, the fetch function correctly identifies that a contact exists (based on email address), but
Zoho Bigin | Adding users to a deal in bigin
Hi, One of our ongoing POCs required adding users to a deal in Bigin. I found that we cannot add individuals using custom fields when we have an Express license. Is there any way to do it?
How to create Comparison across Period chart in a dashboard?
Hi all How can I create this chart in a custom dashboard? The issue for me is that this chart is very small. The CRM module (unlike Projects module) has no ability to expand a chart. I want to make it larger, but also want to include it in a custom Forecast
Validation Rules Trigger on Untouched Fields
In Zoho Desk, validation rules trigger for ALL fields during an update—even fields that weren't modified in the current edit. This behavior is fundamentally different from Zoho CRM and other Zoho products, where validation rules only apply to fields actually
Add Subform Field based on Record Field
Hi All, I am struggling with finding a solution that can populate a subform field based on an existing field on the record. Use case is I have added Current Exchange Rate on a quote as a custom field, I then have a subform with Quoted items that include
CREATE REPORT USING TWO FORMS
ONE FORM CONTAINS LIST OF ALL CLIENTS WHOSE RETURN IS FILED AND OTHER FORM CONTAINS LIST OF RETURNS FILED YEARWISE. NOW I REQUIRED A REPORT OF ALL CLIENTS WHOSE RETURN ARE PENDING FOR A PARTICULAR YEAR
Custom Function : Copy multilookup field to text field
Hi, I'm a newbie on function programming, I try to copy text from a multi lookup field named "societe" to a text field named "societe2". I've used this code. In deluge script it seems to work, but when I trigger this function it doesn't work (Societe2
Make other sub fields mandatory, if first subfield is not empty
I am not finding an option in the rules to make other sub fields mandatory if the first sub field is not empty. I am using the name field to collect parts info, with sub fields of Part Name, Part Number, and Quantity. But I don't want these mandatory
How to normalize CRM module when integrating with Survey?
This question is about the problem with many-to-many relationships and Survey. One of the things our organization does is track people in our program and their jobs. We get new information from the people three times annually through Zoho Surveys. Survey
Poor Search Results on Zoho CRM
The search on Zoho CRM is quite poor. Salesforce has now published a new search, when will get this on Zoho? https://help.salesforce.com/s/articleView?id=data.c360_a_hybridsearch_index.htm&type=5
Parentheses in System Path
Zoho WorkDrive includes a mandatory parenthesis with the organization name in the desktop sync client. This adds parens to the system path. Many command-line applications do not allow for the use of parenthesis, so if you want to use a file saved on WorkDrive in a command line you cannot. Most major document syncing platforms do not allow parenthesis for this reason.
Introducing Assemblies and Kits in Zoho Inventory
Hello customers, We’re excited to share a major revamp to Zoho Inventory that brings both clarity and flexibility to your inventory management experience! Presenting Assemblies and Kits We’re thrilled to introduce Assemblies and Kits, which replaces the
Associating Multiple Work Orders with a Single Invoice in Zoho FSM
Hello Everyone, Is it possible to associate multiple Work Orders with a single Invoice in Zoho FSM? Best Regards, Subhash Kumar
Client Script Quality of Life Improvements #1
Since I'm doing quite a bit of client scripting, I wanted to advise the Zoho Dev teams about some items I have found in client script that could be improved upon. A lot of these are minor, but I feel are important none-the-less. Show Error on Subform
[Webinar] Evolving BI & Analytics in the Age of AI
Artificial intelligence is redefining how data is collected, analyzed, and leveraged across industries. As businesses strive to become more agile and insight-driven, traditional BI and analytics must transform to meet new demands. AI-first organizations
請求書に添付されているファイルをAPI経由で取得する際の問題について
Books APIリファレンス 現在、Books APIを利用して請求書内の添付ファイルを取得するメソッドを構築しています。以下のコードを参考にしているのですが、添付ファイルが複数アップロードされている場合、responseにおいて2つ目のファイルの情報しか取得できない現象が発生しています。 headers_data = Map(); headers_data.put("Authorization", "Zoho-oauthtoken 1000.41d9xxxxxxxxxxxxxxxxxxxxxxxxc2d1.8fccxxxxxxxxxxxxxxxxxxxxxxxx125f");
Zoho CRM and Books Integration
Evening, I have started the integration with FSM from CRM and having difficulties with the mapping. In CRM we use "Unit Price" as our cost price and mark this up on a subform to create a "Sell Price" this markup can be different on each quote depending
Leadchain into a custom module
Hello ZOHO Community ! is it possible to put the leads collected by leadchain into a custom module instaed of leads module ? Best wishes Niels
Zoho Flow to Azure Devops
hi, i'm getting this when trying reauthorize the connection from Zoho Flow to Devops but when i look in Devops there is no service connection related to Zoho So i do not know where i have to renew the mentioned client secret..
533 Relaying disallowed
When I try to send an email from my Zoho account, it gives me that error 533: relaying disallowed. What should I do? Please help.
Formula Module how to convert to percentage
Hello There, I have create a formula field and i want the outcome to be in percentage how do i do that This is my formula ${Deals.Forecast Revenue Per Year}/${Deals.Annual Processing Volume} I have try ${Deals.Forecast Revenue Per Year}/${Deals.Annual
Sandbox - Your Secure Testing Space in Zoho Projects
Managing projects often involves fine-tuning processes, setting up new automations, or making configuration changes. Making those changes directly in a live environment leaves no room for trial and error. Sandbox in Zoho Projects is a dedicated testing
Zoho CRM Functions 53: Automatically name your Deals during lead conversion.
Welcome back everyone! Last week's function was about automatically updating the recent Event date in the Accounts module. This week, it's going to be about automatically giving a custom Deal name whenever a lead is converted. Business scenario Deals are the most important records in CRM. After successful prospecting, the sales cycle is followed by deal creation, follow-up, and its subsequent closure. Being a critical function of your sales cycle, it's good to follow certain best practices. One such
Next Page