All SolutionsChurned User Classifier

Churned User Classifier

Churned User Classifier

Agent designed to classify churned user records from CRM into categories based on associated CRM and Desk data.

ZOHO CRMSALESOPENAIEASY

 

The churned user classifier agent takes churned user records from CRM and classifies them into a given set of categories based on associated CRM and Desk data.

Agent Overview

Purpose
Analyze churned customer data and automatically categorize users into meaningful segments to help teams understand churn patterns, identify risk factors, and make better retention and product decisions.
Products
Zoho CRM, Zoho Desk
Best suited for
Customer success, retention, and support teams that want to quickly understand why users churned
Complexity
Easy
Deployment
Connection, tied to a workflow which activates the function whenever a new record is created in the Churn Users module.
Trigger
Worklfow
Tools
updateSingleChurnedCustomerRecord (custom tool)
Knowledge base
Model Configuration 
GPT, 4o Mini
Data sensitivity
Avoid providing full edit access to the module since it can risk data corruption. 

How the agent works

This agent is embedded within a workflow function that automatically creates a churn user in the Churned Users module when a customer's status changes to "Inactive" in the Subscriptions module. Each time a new churned user record is created, the agent is triggered, which updates the "Churn Category" picklist field in the Churned Users module using the data from the record, notes, or tickets. This data from CRM or Desk could be cancellation reasons, notes, duration between first purchase and cancel date, employee size, or company revenue.

Implementation Guide

Prerequisites

  • Workflow setup
  • Workflow function (provided in this guide)
  • Agent access to get records from the Churned Users module
  • Agent integration with Zoho Desk in order to obtain tickets raised from a certain email address

Agent Instructions

You'll be given a record ID of a record from Zoho CRM's Churned Customers module. The prompt will include all notes associated with this record, along with cancellation reasons.

  • Analyze all the information you've gained and ascertain why the customer has cancelled their subscription. Classify them into one of these brackets:
  • Moved to bundle, if the customer has moved to CRM Plus, Zoho One, or Bigin. Not needed, if the customer has expressed that they have no use for the product.
  • Onboarding issues, if the customer has struggled with onboarding primarily
  • Implementation struggles, if the customer has faced major issues with implementation
  • Support issues, if the customer has had the biggest problems with support
  • Product Issues, if the customer finds the product lacking
  • Budget and price, if the customer has cancelled out of budget concerns or considers the product too expensive
  • Uncategorized, if the customer does not fit well into any other bucket

Classify them exactly into one of these, then update the record using the updateSingleChurnedCustomerRecord tool with the record ID you were first given, and the right classification in the Churn_Category field.

You MUST select exactly one—with the same text—of the categories provided above. Use the Uncategorized option if none of the others make sense.

Custom tool

Tool name: updateSingleChurnedCustomerRecord

YAML:

openapi: 3.0.1
info:
  title: Update a specific Churned Customer record
  description: Use this API to update a specific record in the Churned Customer module.
  version: 1.0.0
servers:
- url: https://www.zohoapis.com/crm/v8
security:
- OAuth2:
  - ZohoCRM.modules.ALL
paths:
  /Churned_Customers/{record_id}:
    put:
      summary: Update a specific Churned Customer record
      description: Use this API to update a specific record in the Churned Customer
        module.
      operationId: updateSingleChurnedCustomerRecord
      parameters:
      - name: record_id
        in: path
        description: Unique ID of the contact record to update.
        required: true
        style: simple
        explode: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                data:
                  type: array
                  items:
                    type: object
                    properties:
                      Churn_Category:
                        type: string
                        enum:
                        - Moved to Bundle
                        - Not Needed
                        - Onboarding Issues
                        - Implementation Struggles
                        - Support Issues
                        - Product Issues
                        - Budget and Price
                        - Uncategorized
        required: true
      responses:
        "200":
          description: Record updated successfully.
        "202":
          description: Request accepted. Record will be updated asynchronously.
        "400":
          description: Bad Request – Invalid or missing parameters.
        "401":
          description: Unauthorized – Invalid or expired OAuth token.
        "403":
          description: Forbidden – User does not have permission to update the record.
        "404":
          description: Record not found.
components:
  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://accounts.zoho.com/oauth/v2/auth
          tokenUrl: https://accounts.zoho.com/oauth/v2/token
          scopes:
            ZohoCRM.modules.ALL: ALL access to ZohoCRM modules

 

Custom function for the CRM workflow

void automation.classifyChurn(String recordId)
{
refresh_token = "1000.0230134cdb95b68d8e426d13c306fb09.f8b01aca016846361b2453e5b3b7e87c";
client_id = "1000.LN6SXTO08UNHYQ6GMKI9Q2SJN81CQP";
client_secret = "c0cdd9fd59784fe7007cc39b55049e509aaf33ab95";
params = Map();
params.put("client_id",client_id);
params.put("client_secret",client_secret);
params.put("refresh_token",refresh_token);
params.put("grant_type","refresh_token");
response = invokeurl
[
url :"https://accounts.zoho.com/oauth/v2/token"
type :POST
parameters:params
];
churnRecord = zoho.crm.getRecordById("Churned_Customers",recordId);
subRec = churnRecord.get("Subscription");
accRec = churnRecord.get("Account");
info accRec;
accId = accRec.get("id");
subId = subRec.get("id");
accountRecord = zoho.crm.getRecordById("Accounts",accId);
subscriptionRecord = zoho.crm.getRecordById("Subscriptions",subId);
accNotes = zoho.crm.getRelatedRecords("Notes","Accounts",accId);
info accNotes;
subNotes = zoho.crm.getRelatedRecords("Notes","Subscriptions",subId);
email = accountRecord.get("Email");
tickets_text = "";
desk_headers = Map();
desk_url = "https://desk.zoho.com/api/v1/tickets/search?email=" + email;
desk_headers.put({"orgId":"893530194"});
desk_response = invokeurl
[
url :desk_url
type :GET
headers:desk_headers
connection:"desk_read"
];
tickets = desk_response.get('data');
for each ticket in tickets
{
tickets_text = tickets_text + ticket.get('subject');
tickets_text = tickets_text + ticket.get('description');
}
moduleName = "Churned_Customers";
access_token = response.get("access_token");
message = "Classify the customer based on this information. Record id " + recordId + "Notes:" + subNotes + accNotes + "cancel reasons:" + subRec.get("Cancel_Reason") + subRec.get("Cancel_Reasons") + subRec.get("Cancel_Comments") + "Tickets: " + tickets_text;
systemArgs = Map();
updateRecordMap = Map();
updateRecordMap.put("record_id",recordId);
systemArgs.put("updateSingleChurnedCustomerRecord",updateRecordMap);
query_url = "https://ziaagents.zoho.com/ziaagents/api/v1/agents/query";
headersMap = Map();
headersMap.put("X-ZIAAGENTS-ORG","892728477");
headersMap.put("X-ZIAAGENTS-AGENT-ID","926000000059319");
headersMap.put("X-ZIAAGENTS-AGENT-VERSION-ID","926000000145025");
headersMap.put("Authorization","Zoho-oauthtoken " + access_token);
headersMap.put("Content-Type","application/json");
bodyMap = Map();
bodyMap.put("query",message);
bodyMap.put("systemArgs",systemArgs);
query_response = invokeurl
[
url :query_url
type :POST
body:bodyMap.toString()
headers:headersMap
];
queryDat = query_response.get("data");
info query_response;
}

Test checklist

  • Provide the agent access to edit only that one field that holds the classification result (i.e., during tools param mapping, set the field param to constant.
  • If you want to provide full edit access to the module, it's advisable to make regular data backups in order to avoid permanent data loss.

Output