Kaizen 233 Generating AI-Powered Follow-up Emails Using CRM Functions and Widgets

Kaizen 233 Generating AI-Powered Follow-up Emails Using CRM Functions and Widgets


Hey everyone!
Welcome back to another interesting post in the Kaizen series!

Sales teams regularly capture interaction notes in CRM after speaking with prospects. However, drafting a follow-up email that reflects the conversation context can be repetitive and time-consuming.

What if CRM could automatically generate a contextual follow-up email draft based on recent interactions with the lead?

In this Kaizen post, we will create a practical tool that generates context-aware follow-up emails directly from a Lead record. You will see how it pulls recent notes, lead's details, and crafts a ready-to-send draft.
The solution uses three Zoho strengths:
  1. Widgets for intuitive UI
  2. Functions for secure backend processing
  3. External AI APIs (like Groq or OpenAI)
The output displays in the widget. Review, tweak if needed, copy, and paste into your email composer.

The business challenge

Sales reps log calls like "Prospect interested in 20 user licenses but concerned about pricing. Explore volume discounts."
Now, crafting a response involves recalling details, matching tone, and ensuring professionalism. Miss a nuance, and trust erodes.
Our AI generator automates this. It scans the lead's details (name, company, status) and the three most recent notes, then prompts an AI model for a draft.

Example output
InfoSubject: Follow-up on CRM Licensing Discussion
Hi John,
Thanks for the insightful chat today about your team's CRM needs. You're considering licenses for around 20 users, and I appreciate pricing being a top priority.
I'll review our volume options and discount eligibility right away. Expect specifics by EOD tomorrow.
Best regards,
[Your Name]

This keeps the rep in control while slashing draft time from 10 minutes to 10 seconds. Scalable for high-volume teams.

High-level architecture

Think of a layered cake!
  1. UI Layer (Widget): Button launches it; displays, copies the draft, and sends the email.
  2. Logic Layer (Function): Fetches CRM data server-side, builds prompt, calls AI securely.
  3. AI Layer: External service generates natural language.

Flow

Rep clicks a button → Widget gets Lead ID → Calls function → Function queries CRM + AI → Draft back to widget

Secure (no client-side keys), reliable (server-side), and reusable across modules.

Developers often wonder "Why not fetch AI responses straight from widget JavaScript?"

Widgets run in a browser iframe, so embedding API keys exposes them to inspection or misuse. CORS policies can also block external requests, and complex logic like prompt building suits server-side better.
So, the best practice is to use widgets for UI and functions for secure server-side work. This hides credentials and reuses logic.

Follow these steps to build this solution.

Step 1: Add the custom Button

  1. Head to SetupModules and FieldsLeadsButtonsCreate New Button.
  2. Provide the following details:
    1. Name: Generate AI Email
    2. Placement: Record Detail Page (top or bottom)
    3. Action: Open Widget (select your widget once built)
  3. Save and add to layout.
  4. Test: Button appears on Leads; clicking loads widget in context.

Idea
Pro tip: Position near the "Send Email" button for seamless workflow.

Step 2: Build the CRM Function

  1. Go to SetupDeveloper HubFunctions+ Create Function.
  2. Enter the display name, function name, description, choose Standalone as the category.

  3. Click Create. The Functions IDE opens.
  4. Enter the following function code. Note that this example uses Groq AI API.
    string standalone.generate_ai_email(String leadId)
    {
    lead = zoho.crm.getRecordById("Leads",leadId);
    name = ifnull(lead.get("Full_Name"),"");
    company = ifnull(lead.get("Company"),"");
    status = ifnull(lead.get("Lead_Status"),"");
    // Fetch recent CRM Notes
    notesResp = zoho.crm.getRelatedRecords("Notes","Leads",leadId);
    recentNotes = "";
    count = 0;
    for each  note in notesResp
    {
    noteContent = ifnull(note.get("Note_Content"),"");
    if(noteContent != "")
    {
    recentNotes = recentNotes + "[" + (count + 1) + "] " + noteContent + ". ";
    }
    count = count + 1;
    if(count == 3)
    {
    break;
    }
    }
    // Build AI prompt
    prompt = "You are a sales assistant helping draft follow-up emails. ";
    prompt = prompt + "Generate a professional follow-up email based on the customer's previous interactions. ";
    prompt = prompt + "Lead Name: " + name + ". ";
    prompt = prompt + "Company: " + company + ". ";
    prompt = prompt + "Lead Status: " + status + ". ";
    prompt = prompt + "Recent CRM Interaction Notes: " + recentNotes + ". ";
    prompt = prompt + "Write a professional follow-up email referencing the discussion.";
    // Sanitize prompt
    prompt = prompt.replaceAll("\"","\\\"");
    prompt = prompt.replaceAll("\n"," ");
    prompt = prompt.replaceAll("\r"," ");
    // Build AI request
    message = Map();
    message.put("role","user");
    message.put("content",prompt);
    messages = List();
    messages.add(message);
    requestBody = Map();
    requestBody.put("model","llama-3.1-8b-instant");
    requestBody.put("messages",messages);
    requestBody.put("temperature",0.4);
    requestJSON = requestBody.toString();
    // Call Groq API
    response = invokeurl
    [
    type :POST
    parameters:requestJSON
    headers:{"Authorization":"Bearer YOUR_API_KEY","Content-Type":"application/json"}
    ];
    // Extract AI email
    emailContent = "";
    if(response.containsKey("choices"))
    {
    emailContent = response.get("choices").get(0).get("message").get("content");
    }
    // Return generated email to widget
    return emailContent;
    }

Notes
Note for production
  1. Connections: In the example, the API key is included directly in the function for simplicity. However, production deployments should use Connections instead of hard-coding credentials as they allow developers to store API credentials securely within Zoho CRM and reuse them across multiple integrations.
  2. Error Handling: Wrap invokeurl in try-catch.
  3. Limits: Cap notes at 3 to avoid token overflow and tune temperature (0.4 = consistent).
  4. Logging: Add info prompt before API call for debugging.
  5. Test: Execute with sample Lead ID. Expect clean email string back.
  6. Extend: Add phone/email from Lead, or filter notes by date (e.g., last 7 days via criteria).

Step 3: Develop the Widget

The widget provides the user interface for generating and copying the AI email.
When the widget loads, it retrieves the current Lead ID using the CRM widget JS SDK. When the user clicks Generate Email, the widget calls the CRM function and displays the generated email draft.
The widget also provides Send Email and Copy Email buttons that send the email to the lead record's email ID and copies the email content to the clipboard so the rep can past it into the email composer, respectively.

Sample index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>  <!-- Add your style here-->
</style>
</head>
<body>
<div class="widget-card">
  <div class="widget-header">
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M20 4H4C2.9 4 2 4.9 2 6v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" fill="white"/>
    </svg>
    <h2>AI Follow-up Email Generator</h2>
  </div>
  <div class="widget-body">
    <div class="email-box-wrapper">
      <textarea id="emailBox" placeholder="Click 'Generate Email' to create a contextual follow-up email..."></textarea>
    </div>
    <div class="action-bar">
      <button class="btn btn-primary" id="generateBtn" onclick="generateEmail()">
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none"><path d="M12 2l2.4 7.4H22l-6.2 4.5 2.4 7.4L12 17l-6.2 4.3 2.4-7.4L2 9.4h7.6z" fill="white"/></svg>
        Generate Email
      </button>
      <button class="btn btn-success" id="sendBtn" onclick="sendEmail()">
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none"><path d="M2 21l21-9L2 3v7l15 2-15 2v7z" fill="white"/></svg>
        Send Email
      </button>
      <button class="btn btn-secondary" onclick="copyEmail()">
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none"><path d="M16 1H4C2.9 1 2 1.9 2 3v14h2V3h12V1zm3 4H8C6.9 5 6 5.9 6 7v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" fill="#3D4354"/></svg>
        Copy
      </button>
    </div>
    <div class="status-bar" id="statusBar"></div>
  </div>
</div>
<script src="index.js"></script>
</body>
</html>

Sample index.js

let leadId;
let leadEmail;
let generatedEmail = "";

// Widget initialization
ZOHO.embeddedApp.on("PageLoad", function(data){

    if(data && data.EntityId){

        leadId = data.EntityId[0];

        // Fetch Lead email
        ZOHO.CRM.API.getRecord({
            Entity: "Leads",
            RecordID: leadId
        })
        .then(function(response){

            if(response && response.data && response.data.length > 0){
                leadEmail = response.data[0].Email;
            }

        });

    }

});

ZOHO.embeddedApp.init();


// --- Helper functions used by email generation/sending ---
// function formatEmailText(raw) { /* formatting logic */ }
// function textToHtml(text) { /* convert plain text to HTML */ }
// function setStatus(message, type) { /* UI status logic */ }


// Generate AI email using a CRM function
function generateEmail(){

    var req_data = {
        arguments: JSON.stringify({
            leadId: leadId
        })
    };

    ZOHO.CRM.FUNCTIONS.execute('generate_ai_email', req_data)

    .then(function(response){

        if(response && response.details){

            generatedEmail = formatEmailText(response.details.output);
            document.getElementById('emailBox').value = generatedEmail;

        }

    })
    .catch(function(error){

        console.log('Function error:', error);

    });

}


// Send email using ZRC
async function sendEmail() {

    const emailContent = document.getElementById('emailBox').value.trim();

    if (!emailContent) return;

    try {

        // Get allowed "From" addresses
        const fromRes = await zrc.get('/crm/v8/settings/emails/actions/from_addresses');
        const fromAddress = fromRes.data.from_addresses[0];

        const htmlContent = textToHtml(emailContent);

        // Send mail
        const response = await zrc.post(`/crm/v8/Leads/${leadId}/actions/send_mail`, {

            data: [
                {
                    from: {
                        user_name: fromAddress.display_name || fromAddress.user_name,
                        email: fromAddress.email
                    },
                    to: [
                        {
                            email: leadEmail
                        }
                    ],
                    subject: 'Follow-up',
                    content: htmlContent,
                    mail_format: 'html'
                }
            ]

        });

        console.log('Mail sent:', response);

    } catch (error) {

        console.error('Send mail error:', error);

    }

}

// Copy generated email
// function copyEmail(){ /* copy logic */ }
Info
The  ZIP containing the complete index.js and index.html files used in this example is attached at the end of this post.

Refer to Creating a Widget in Zoho CRM and the JS SDK for more details on CLI installation, creating, packaging, and hosting a widget.

User experience walkthrough

  1. The salesperson clicks Generate AI Email on the record detail page and the widget opens.
  2. The salesperson clicks the Generate AI email button. The widget retrieves CRM context and generates a contextual follow-up email draft.
  3. The email appears inside the widget, allowing the salesperson to quickly review the generated message, and use the Send Email button to send it to the lead's email ID.
This workflow ensures that AI assists with drafting the message while still allowing the salesperson to review and control the final communication.

Here is a GIF explaining the use case in action.


Key takeaways

This example demonstrates how Zoho CRM developers can integrate generative AI into CRM workflows using platform-native tools.
Combine CRM Widgets for user interaction and Functions for secure server-side processing to safely integrate external AI services without exposing credentials or compromising security.

The same architecture can be extended to build more advanced AI features, such as:
  1. Automated meeting summaries
  2. Deal health insights
  3. Proposal drafting assistants
  4. Intelligent follow-up recommendations

We hope you liked this post. Let us know what you think in the comments or reach out to us at support@zohocrm.com.
Cheers!



===================================================================================



    • Sticky Posts

    • Kaizen #198: Using Client Script for Custom Validation in Blueprint

      Nearing 200th Kaizen Post – 1 More to the Big Two-Oh-Oh! Do you have any questions, suggestions, or topics you would like us to cover in future posts? Your insights and suggestions help us shape future content and make this series better for everyone.
    • Kaizen #226: Using ZRC in Client Script

      Hello everyone! Welcome to another week of Kaizen. In today's post, lets see what is ZRC (Zoho Request Client) and how we can use ZRC methods in Client Script to get inputs from a Salesperson and update the Lead status with a single button click. In this
    • Kaizen #222 - Client Script Support for Notes Related List

      Hello everyone! Welcome to another week of Kaizen. The final Kaizen post of the year 2025 is here! With the new Client Script support for the Notes Related List, you can validate, enrich, and manage notes across modules. In this post, we’ll explore how
    • Kaizen #217 - Actions APIs : Tasks

      Welcome to another week of Kaizen! In last week's post we discussed Email Notifications APIs which act as the link between your Workflow automations and you. We have discussed how Zylker Cloud Services uses Email Notifications API in their custom dashboard.
    • Kaizen #216 - Actions APIs : Email Notifications

      Welcome to another week of Kaizen! For the last three weeks, we have been discussing Zylker's workflows. We successfully updated a dormant workflow, built a new one from the ground up and more. But our work is not finished—these automated processes are
      • Recent Topics

      • Mastering Zia Match Scores | Let's Talk Recruit

        Feeling overwhelmed by hundreds of resumes for every job? You’re not alone! Welcome back to Let’s Talk Recruit, where we break down Zoho Recruit’s features and hiring best practices into simple, actionable insights for recruiters. Imagine having an assistant
      • Zoho Books - France

        L’équipe de Zoho France reçoit régulièrement des questions sur la conformité de ses applications de finances (Zoho Books/ Zoho Invoice) pour le marché français. Voici quelques points pour clarifier la question : Zoho Books est un logiciel de comptabilité
      • Zoho Home Tasks View - any way to add conditional formatting?

        Hello! Is there any way to add conditional formatting to the tasks window under Home Page? Id like tasks to be shown in green/yellow and red depending on its task date. I know Classic View shows colorfull icons: But its not possible to add any custom
      • User Name in Zoho Cliq Not Updating Across Apps?

        We updated the name of a user in Zoho. (From Sue to Taylor) Her name has not been updated in Cliq on all apps. When in Zoho One, if I go to Cliq directly, it is correct, but if I am in another app, and the Cliq bar pops up on the bottom, it will be the
      • Set Default Status of Assembly to "Assembled" When Entered in UI

        I've just discovered the new "confirmed" status of Assemblies within Inventory. While I understand the intent of this (allowing for manufacturing planning and raw material stock allocation), it was initially confusing to me when manually entering some
      • New Customization options in the module builder: Quick Create and Detail view

        Hello everyone, We have introduced two new components to the module builder: Quick create and Detail view. The Quick Create Component It is a mini form used to create a record and associate it to the parent record from a lookup field. For example, if you have a Deals lookup in the Contacts module, then you can associate existing deals or create a deal and associate it with the contact. You can customize this Quick Create form by adding standard as well as custom fields. There is no limit to the number
      • Items Landed Cost and Profit?

        Hello, we recently went live with Zoho Inventory, and I have a question about the Landed Cost feature. The FAQ reads: "Tracking the landed cost helps determine the overall cost incurred in procuring the product. This, in turn, helps you to decide the
      • How do I automate my package and shipment process

        Hey, I have a very basic problem regarding automating my package and shipment. My business does not require me to have those modules and manage them currently and since I'm new to zoho I want to start with just the basics. I just need to automatically
      • Announcing Zoho Sheet desktop app for macOS and Windows (Beta)

        Hello Sheet users, We know you’ve been waiting for this one. It has always been the top priority on our roadmap to provide a single native desktop app for macOS and Windows that works both online and offline. Today, we are excited to announce that the
      • zoho creator panel add formula

        Hello. I'm using zoho panels which works good for me but i need to add an extra calculation the predefined sum function provided by zoho. not sure if this is possible? The code zoho is generating for the sum is as follows; <text margin='0px' padding='0px'
      • How to take result of calculation in a form and subtract from a value in another form

        I'm using the following code on successful form submission to perform a calculation and enter the result into a field a shown below input.Space_Used = ifNull(input.Area_In_Square_Meters,0) * ifNull(input.Total_Stock,0); I now need the result above, the
      • Re: Application Architecture in Zoho Creator — A Platform-Specific Deep Dive

        A recent community post on application architecture made some excellent points about planning architecture early in Zoho Creator projects. The core message is right — Creator applications have a habit of growing organically into maintenance nightmares,
      • Missing Fields in Lead Conversion Mapping

        I’ve been using Zoho CRM since 2013 to manage leads and convert them into contacts and jobs. When I convert a lead to a customer, I create a new contact and a job. Previously, this process automatically transferred key information from the lead to the
      • Mass email from Report output

        Hi, I'd like to send a mass email based on a report output. The report is pulling multiple information from linked modules. Each line of the report ends up with a contact name, email and multiple field values pulled from the linked modules (some are custom
      • What's New in Zoho Analytics - February 2026

        Hello Users! We're back with another round of updates for Zoho Analytics. This month's release focuses on giving you greater flexibility in how you visualize, manage, and act on your data - with new features like custom visualizations, remote MCP server,
      • Understanding tasks

        So we have a module linked to contacts called Equipment and that in turn has a related module called service calls. Often on a service call we want add a task but we are not able. I think it needs a link to the contact. WE therefore tried to add tasks
      • Cross Module Filtering – Use Fields from Lookup modules in Custom Views criteria and Advanced Filters

        Hello everyone, Zoho CRM now enables you to achieve deeper filtering of records in a module, using fields of a lookup, thereby enhancing your data management experience manifold. This filtering based on lookup module fields is now available in advanced
      • Related Lists in Email Templates

        Hi Zoho team, I would love to see a feature where related list information can easily be added to an email template (instead of mail merge). I have a client who books flights for their customers. They do this through a Flights module so all booked flights
      • Auto-sync field of lookup value

        This feature has been requested many times in the discussion Field of Lookup Announcement and this post aims to track it separately. At the moment the value of a 'field of lookup' is a snapshot but once the parent lookup field is updated the values diverge.
      • Introducing SlyteUI : From Idea to a Working Interface in Minutes

        Hello everyone! Are you spending hours building basic UIs? Does even the smallest customization feel like a major task? CRM customization should feel intuitive and straightforward, not time consuming or exhausting. SlyteUI makes this possible by simplifying
      • need to add tax in servce and items

        please give access to add gst or tac to add in service and parts
      • Blueprint transitions on locked records

        We use the ability to automatically lock records (quotes, sales orders, etc.) based on criteria, such as stage. For instance, if a quote has been sent to a client, the quote is then locked for further edits. Our ideal quote stage process is: Draft>Sent>Won.
      • Lead Entry Strategy

        Hi there. Was wondering if anybody could share their strategy around lead entry into Zoho CRM. One of the challenges with Zoho CRM is a lead requires a last name. We want to capture companies that operate within our addressable market, and we often don't
      • Social posts not saving as drafts or posting

        Over the past two days, I have written and modified the same post for 5 different channels 4 different times. They will neither save as draft, nor go through and load in the scheduler, nor will they post when I try to push it live now. In every case,
      • Introducing user groups, roles, and profiles in Zoho Sign

        Hello, Today, we're excited to unveil user groups, roles, and profiles to offer granular access controls in Zoho Sign. These help administrators manage document visibility and accessibility based on assigned user roles and permission sets. User groups:
      • On Duty Requests - Zoho People Data

        Hello Team, We are currently using the On Duty Form to record Work From Home (WFH) requests in our organization. However, we are facing an issue where pending On Duty requests are not appearing in the Attendance Module. For example, if I submit On Duty
      • Add org-specific data with Custom modules

        Zoho Sprints offers a wide range of default modules designed to support agile project management. However, some projects or organizations may require additional modules to store project-specific or organization-specific data. To address this, Zoho Sprints
      • All new Address Field in Zoho CRM: maintain structured and accurate address inputs

        Availability Update: 29 September 2025: It's currently available for all new sign-ups and for existing Zoho CRM orgs which are in the Professional edition exclusively for IN DC users. 2 March 2026: Available to users in all DCs except US and EU DC. 24
      • 【無料/オンライン】4/30開催|Zoho ワークアウト(オンライン勉強会)

        ユーザーの皆さま、こんにちは。 コミュニティグループの中野です。 4月開催の「Zoho ワークアウト」のご案内です。 本イベントは、Zohoユーザー同士で交流しながら 設定・運用・活用に関する課題解決を目指すオンライン勉強会です。 「設定を進めたいけれど、一人だと手が止まってしまう」 「他社がどう活用しているのか知りたい」 「相談できる相手がおらず、運用に悩んでいる」 そんな方におすすめのイベントです。 ▶︎参加登録はこちら(無料) https://us02web.zoom.us/meeting/register/qIH1ut8aTnq-R7-21eF3BQ
      • Give your organization a branded login experience with White Labeling

        When users log in to their email, the URL they land on is often the first thing they notice. A generic login page can feel disconnected from your organization's identity, especially when you have already established a strong brand presence. For administrators,
      • Archive Option in Conversation View

        Hello, I have a suggestion\request to add an "Archive Thread" button in conversation view of Zoho Mail. The best suggestion I have is to put an "Archive Thread" button next to the "Label Entire Thread" button in conversation view. Most users don't just
      • Search API filter/sort ignores comment-triggered modifiedTime updates

        Summary When a comment is added to a Call or Account, the parent record's modifiedTime is correctly bumped. This bumped value is visible in: GET /api/v1/calls/{id} ✅ GET /api/v1/calls/search without a filter ✅ — the record's response body shows the new
      • Automation#17: Auto-Create Tasks in Zoho Projects Upon Ticket Creation in Zoho Desk

        Hello Everyone, This edition delivers the solution to automatically create a task in Zoho Projects when a ticket is created in Zoho Desk. Zylker Resorts uses Zoho Desk for bookings and handling guest requests. Zylker resorts outsources cab bookings to
      • Mail Merge in Zoho Desk

        Hello Team, Do we have an option of mail merge within Zoho Desk like we have in Zoho CRM? We have a requirement to generate some pdf file in the form of mail merge doc from the ticket.
      • No DINAR (Serbia) in currency list

        There is no Serbian Dinar listed in the currency list. Dinar is available in Books, Inventory, CRM, FSM. Please make it available in Desk as well. Without it, the integration with other apps is not possible.
      • CRM Notes

        Hello, We want to add a Note to the Contact record when a Note is added to a Case or Deal. I wasn't able to do this using a workflow, so I tried using Zoho Flow, but that didn't work either. Does anyone have a suggestion on how we can accomplish thi
      • Zoho Forms for Vertical Studio Subscriber Organizations

        We’re excited to introduce the Zoho Forms integration with Vertical Studio. This allows you to capture data using forms and send it directly to your Vertical Studio modules. With this integration, form submissions can be used to create or update records,
      • Using Email Triggers on Zoho Flow

        Hello, I'm sending the email to create the variables as this article says: https://help.zoho.com/portal/en/kb/flow/user-guide/create-a-flow/articles/email-trigger#How_email_trigger_works But the collection of the variables only seems to work when the
      • How to transfer uploaded files from Zoho Survey to external systems?

        Hi everyone, I'm currently integrating Zoho Survey with an external system and have run into a limitation I haven't been able to solve yet. I'm using webhooks to send submitted survey data (JSON) to my external API, which works perfectly for all standard
      • Re-Apply SLA When Ticket Reopened from Closed Status?

        If you have an SLA applied, timers are deactivated when going to "On Hold" status type and reactivated when going back to an Open status type. What we discovered is when a customer replies to a closed case and it reopens, the SLA is not applied and timers
      • Next Page