
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
add two date range
Hi, How can I add two date range selections to compare two different column values in a single pivot view? I have attached a snap for your reference.
Announcing new features in Trident for Windows (v.1.26.5.0)
Hello Community, Trident for Windows is here with exciting new features to elevate your email communication and enhance productivity. Let’s dive into what’s new! Open .eml files in Trident. You can now open .eml files directly using Trident. This makes
Adjust The max character in Specification Field
Is there another way to adjust the maximum character limit for the Specifications field in Zoho Commerce? I need to accept responses with fewer than 200 characters.
Customize the Sign In And Sign Up in Zoho Commerce
Is there another way to customize the Sign In and Sign Up in website Zoho Commerce like this i want to customize to edit it like change the "Sign In" word into "Login Zoho Commerce" it is possible or other way to do that?
Territories : Deluge and APIs
I am trying to work out how to filter a deluge query by territory eg "SELECT Total_Amount, Stage, Closing_Date, Created_Time, Deal_Name FROM Deals WHERE Stage in (" + varBaseCriteria + ") AND Territory = 'Territory1'" The problem being that Territory
Tidying up messes file system on Site
I'm been given access to a new site that's been managed by several different people over the years, each with different ways of managing images and files. If I move an image from one folder to another, it shows a missing image icon on the site's page.
Move Archive Button in Zoho Mail to Main Toolbar?
Is there a way to add the Archive Button to this tool bar so I don't have to click the three dots every time?
Introducing Bigin 360: Our new pricing edition with increased feature limits and pre-installed toppings
Dear Biginners Club, Today, we're pleased to launch a brand-new pricing edition called Bigin 360, our highest pricing edition that will sit on top of Express and Premier editions. It's been over four years since our launch, and we're receiving some great
URL field display value
Is it possible to give a URL stored in a project a display value, rather than showing the whole url? I have a lot of projects connected to issues filed on a separate site, each with a distinct URL. For example: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=40000
need payment link excl. TDS Amount.
Dear Team Zoho, Kindly generate a payment link excluding TDS amount. So that TDS can be submitted through portal. Thanks & Regards, Arijit S.
CRM is very slow now
CRM is very slow now. Plz check ASAP
Access error when running invokeurl
Hi, I'm running the following code: string standalone.test_api() { LoroToken=zoho.crm.getOrgVariable("LoroToken"); info "LoroToken:"+ LoroToken; headersMap = Map(); headersMap.put("Authorization", "Bearer "+LoroToken ); headersMap.put("Content-Type",
Disable Column Freeze in PWA View but Keep It on Desktop — Zoho Creator Reports
Zoho Creator offers a useful feature to freeze up to two columns in a report view, which works well on desktops. However, our users access the app on both laptops and mobile devices, and freezing columns makes reports nearly unusable on smaller screens.
How do I add more space to a note in ‘draw’?
I’m taking handwritten notes using the draw note, but I don’t seem to be able to scroll down to get more room on the page. How do I make more room to take notes?
Overtime per week vs. per day
In the United States 90% of the states calculate overtime as working more than 40 hours per week. It appears that Zoho People can only calculate overtime per day. How do we fix this? Here is an example: Mon 8 hours Tues 7 hours Wed 9 hours Thur 8 hours Fri 8 hours ----- Total 40 hours (no overtime) However, Zoho people says 1 hours of overtime because the employee worked 9 hours on Wednesday. Maybe I have something setup wrong in Zoho People?
Add Hebrew Language Support in SalesIQ Idle Chat Handling and Reminders
Dear Zoho SalesIQ Team, Greetings. We would like to request the addition of Hebrew language support in the Idle Chat Handling and Reminders functionality within Zoho SalesIQ. 🗣️ Background & Use Case Currently, we have successfully configured our Zobot
notebook synchronization - problem
Good afternoon, Since yesterday when trying to create a new notebook, it does not let me and when creating the notes they are not synchronized with the cloud, I think the error may be with the encryption of images because in the pages appears the image
Notebook 3.5.0 -- Sort order Name A-Z not working
Just updated to Notebook 3.5.0 on Windows 10. Sort order by Name, A to Z is backwards, like Z to A. Selecting Z to A still works as expected.
Stay organized with chat-to-ticket timers
Hi there! Ever lost track of a customer’s message? Or found yourself scrolling through long chat threads trying to figure out what’s what? Setting up a chat-to-ticket timer can help. It decides when a reply should stay in the old ticket or create a new
New notecards not syncing across devices
Hi, I'm having the same problem where my notes are not syncing from my Android to my laptop. Please help
Please add custom sort in Windows ver. of Notebook!
Dear Zoho, I love the custom sort (drag and order notes) in the Android version of Notebook, but when I sync onto the Notebook on Windows, the note orders all get messed up because it doesn't support custom sorting yet. This makes it impossible to do
Formula to return string "WK 26 - 6.2.25 - 6.8.25
Here's what I've got but syntatic failure: if(not(isnull(${Deals.GS_Due})), "Week " + Tostring(ceil(dayofyear(${Deals.GS_Due}) / 7)) + " - " + Tostring(month(${Deals.GS_Due})) + "." + Tostring(day(${Deals.GS_Due})) + "." + Tostring(year(${Deals.GS_Due}))
Introducing LeadChain in Bigin to sync leads from Social Ads easily
We're excited to introduce a new topping in Bigin called LeadChain by Zoho Social. LeadChain instantly syncs lead information from social media lead ads to Bigin, making it easier to turn them into customers. It also helps you send conversion data back
Templates Access
There should be an option to grant users access to templates but not allow them to edit/delete templates. In setup there is only one tick option for templates. This will give any user access to view as well as delete/edit. This doesnt make sense as they
Generate a Zoho Sign link
From time to time I get a response "I never received your you e-document for electronic signature" is there a way to generate a Zoho Sign link to share.
Power of Automation :: Autocomplete the Project upon Task closure.
Hello Everyone, A Custom function is a user-written set of code to achieve a specific requirement. Set the required conditions needed as when to trigger using the Workflow rules (be it Tasks / Project) and associate the custom function to it. Requirement:
Unable to send email through Gmail
So I had a custom domain email from Zoho and I was able to send emails from this account through my gmail. 2 days ago I added another user. This user is not able to receive any emails, that's the first issue, moreover, even though I haven't changed anything else other than adding this user, now I am unable to send email through my gmail account with the first user either! I have opened a case with priority "I am stuck, need assistance", haven't got a single reply in about 36 hours. I am unable to
"Invalid Credentials(Failure)" when configuring IMAP (Outlook)
Getting the below response when attempting to configure Zoho mail in Outlook 2016: Notes (what I've done so far): Zoho mail on web client is working with no issues. Though I was certain the username & PW were correct (was using same credentials on web client as I was attempting to utilize in Outlook 2016), just in case, I went ahead and changed my password on the web client and re-attempted Outlook configuration; same error response. IMAP for my email account was enabled on the web client (and POP
Custom service report or Zoho forms integration
Hello, So far the experience with Zoho FSM and the integration with Books has been good, however there are limitations with service reports. As with my business, many organisations send technicians to different types of jobs that call for a different
Pre-fill Email field on Zoho Forms
Hello there, How do we automatically pre-fill the email address field of a Zoho Form from the data that is in CRM? Thanks, Joel
Webhook Trigger for New Messages in Cliq Channels
Hello, I would like to request a feature to enable webhook triggers when a new message is added to a Cliq channel. This functionality would allow us to seamlessly send important information from Cliq to other relevant systems. This webhook trigger can
Unable to add subdomain email account under the main email account
Hi, Is it possible to add a subdomain email account, like sky@agent.rebrightrental.com from the Admin Console of admin@rebrightrental.com in Zoho Mail? Or is that not possible? I want to create at least 3, like oak@agent.rebrightrental.com, bliss@agent.rebrightrental.com,
How to delete a large number of emails
Since no one else seems to be asking this, there must be something in the zoho mail interface that I am failing to see. Not looking for an automatic method. Instead, when I sort email by sender, I want to be able to select all emails from a sender and
want to renew my account
I am not getting email - it seems renewal of account is required. pls check and advise how to renew the account and what would be the charges
Mail app and desktop on chrome not syncing properly.
Yesterday I spent a significant amount of time on the computer moving emails to different folders, and deleting several. This morning, I realized on my zoho mail app it still showed all the emails in the inbox. Nothing had synced. I couldn't find a sync
Can't connect to Thunderbird
Setting up Thunderbird I get an error message: "Unable to log in at server. Probably wrong configuration, username or password". The password and email address are definitely correct as I used them to log in to webmail. My settings are definitely correct
Can't receive emails, but can send
I setup a mail using the domain @shopik.io. I set this up using the correct CNAME record on Namecheap. When I tried to send a test mail from my Gmail account it was not able to send and I got the following error; The response from the remote server was:
Unable to send message;Reason:550 5.4.6 Unusual sending activity detected. Please try after sometime
Hello i'm unable to send any email because i keep getting this error Unable to send message;Reason:550 5.4.6 Unusual sending activity detected. Please try after sometime i have literally sent less than 10 emails today i'm not sure why i'm getting this
Partial refunds
I am trying to process refund for a one item invoice, however the refund is partial: i am getting this error while creating credit note, can anyone share some wisdom about this
Tickets are enabled but not showing in zoho desk widget
Hi, I have a Zoho Desk ASAP JWT widget inside my app. I enabled the KB, tickets, and salesIQ chat (screenshot 1), but only the KB and chat are showing on my app widget (screenshot 2). Can you help? Thank you
Next Page