How to Add Subform Rows in Zoho CRM Using Deluge Without Overwriting Existing Data

How to Update Subform field in Zoho Crm

Introduction

A practical, API-free guide for Zoho CRM developers and admins who want to dynamically append subform rows using Deluge scripting — without losing any existing row data in the process.

In This Article

  1. The Problem With Subform Updates in Zoho CRM
  2. Why Add Subform Rows Dynamically?
  3. Prerequisites Before You Begin
  4. Step-by-Step: Adding a Subform Row Without Overwriting
  5. Real-World Scenario: Auto-Calculate a Subform Field
  6. Common Mistakes and How to Avoid Them
  7. When to Use This Approach vs. the API
  8. Conclusion

1. The Problem With Subform Updates in Zoho CRM

If you have spent any time trying to update subform rows in Zoho CRM using Deluge, you have probably run into a frustrating issue: every time you push a new row to a subform, it wipes out all the rows that were already there.

This catches a lot of developers off guard. The natural assumption is that adding a new row means adding to the existing data — the same way you would append a new line to a spreadsheet. But Zoho CRM's subform update mechanism works differently. When you call zoho.crm.updateRecord with a subform field, the system replaces the entire subform with whatever list you provide. If that list only contains your one new row, the rest of the data is gone.

The good news is there is a clean, straightforward solution — and it does not require touching the external API at all. Everything can be handled inside Zoho CRM using Deluge scripting. Once you understand the logic, it becomes a reliable pattern you can reuse across any module and any subform.

ⓘ What is a subform in Zoho CRM? A subform is an embedded table inside a CRM record that lets you store multiple line items. For example, a Quote record might have a subform called Quoted Items where each row represents a different product, its quantity, and its price. Subforms are available in modules like Quotes, Invoices, and any custom module you build.

2. Why Add Subform Rows Dynamically?

Before diving into the code, it is worth pausing on why you might need this in the first place. Dynamic subform updates are useful in more scenarios than most people initially realize. Here are two of the most common use cases:

Activity and Outreach Logging

Imagine you want to track every time a sales rep contacts a Lead — the date, the channel (email, call, LinkedIn), and a short note. Instead of relying on the default activity log, you can create a subform called Outreach Log on the Lead module and append a new row every time a workflow fires. Because you are appending rather than replacing, the history builds up over time exactly like a proper log should.

Cross-Module Product Sales Tracking

Another powerful use case involves syncing subform data across modules. For example, when a Deal is won, you might want to copy the product line items from the Deal's subform into the associated Account's subform — building a cumulative record of everything that customer has ever purchased. Each new won Deal adds rows to the Account without touching the ones already there.

Both of these scenarios share the same core requirement: append new rows without destroying existing ones. That is exactly what this guide solves.

3. Prerequisites Before You Begin

This guide assumes a basic working knowledge of Zoho CRM. Before writing any code, make sure you have the following ready:

  • Access to the Zoho CRM developer console or a workflow/function where you can write Deluge code
  • The API name of the module you are working with (e.g., Quotes, Leads, or a custom module name)
  • The API name of the subform field (e.g., Quoted_Items) — you can find this in CRM Setup → Modules → your module → Fields
  • The API names of each subform column you want to write to (e.g., Product, Quantity, Price)
  • The Record ID of the record you want to update
⚠ Find API names correctly Field labels (the names you see on screen) are not always the same as API names. Always verify API names in the CRM setup panel before writing your code. Using the wrong name will cause your function to silently fail or update the wrong field.

4. Step-by-Step: Adding a Subform Row Without Overwriting

The approach follows a four-step pattern. Once you understand it, you can adapt it to almost any subform update scenario in Zoho CRM.

1 Create a Map for the New Row

Start by building a Map object that represents the new row you want to add. Each key in the Map should match the API name of a subform column, and each value is the data you want to write into that cell.

Think of this Map as a single row in the subform — one key-value pair per column.

updateMap = Map();
updateMap.put("Product", "Apples");
updateMap.put("Quantity", 50);
updateMap.put("Price", 2);

Replace "Product", "Quantity", and "Price" with your actual subform column API names.

2 Add the New Row Map Into a List

Zoho CRM expects a List when updating a subform — not a single Map. Create a List and add your new row Map into it. This List will eventually hold both the new row and all the existing rows.

updateList = List();
updateList.add(updateMap);
3 Fetch and Preserve the Existing Subform Rows

This is the critical step that prevents data loss. Before updating, you need to retrieve the current subform rows from the record and add them to your List. The key detail is that you must include each existing row's id when re-adding it — this tells Zoho CRM to keep that row rather than create a duplicate.

// Fetch the existing record
record = zoho.crm.getRecordById("Quotes", "683769000000338181");

// Get the subform data using its API name
subform = record.get("Quoted_Items");

// Loop through existing rows and add each one back by its ID
for each s in subform
{
    updateList.add({"id": s.get("id")});
}

A few things to note here:

  • Replace "Quotes" with your module API name
  • Replace "683769000000338181" with your actual record ID (or a variable holding it)
  • Replace "Quoted_Items" with the API name of your subform field
  • You only need to pass the id for existing rows — Zoho CRM will retain all their other field values automatically
4 Update the CRM Record With the Complete List

Now that your List contains both the new row and references to all existing rows, pass it to zoho.crm.updateRecord. Zoho CRM will process the full list — preserving existing rows by their IDs and creating the new row as a fresh entry.

updateCRM = zoho.crm.updateRecord(
    "Quotes",
    "683769000000338181",
    {"Quoted_Items": updateList}
);
info updateCRM;
✓ That's it. After this runs, the subform will contain all the rows that were there before, plus the new one you just added. Nothing gets erased.

5. Real-World Scenario: Auto-Calculate a Subform Field

Let's take the concept further with a practical example. Say you have a Quoted_Items subform with three fields: Quantity, Standard_Price, and Extended_Standard_Price. The last field is supposed to hold the result of Quantity * Standard_Price, but you want it calculated and written automatically via code instead of relying on a formula field.

Here is the Deluge function that does this using invokeurl to fetch the record (which gives more consistent subform data in some CRM versions) and then iterates through each row to compute and update the calculated field:

// Replace quoteId with your actual Quote record ID variable or value
fetch_record = invokeurl
[
    url: "https://www.zohoapis.in/crm/v6/Quotes/quoteId"
    type: GET
    connection: "fetch_data_1"
];

// The response wraps records inside a "data" list
information = fetch_record.get("data");

for each rec in information
{
    quoted_items = rec.get("Quoted_Items");
    extended_standard_prices = List();

    for each item in quoted_items
    {
        subform = Map();

        // Always preserve the existing row ID
        subform.put("id", item.get("id"));

        // Safely fetch values — default to 0 if null
        quantity = ifnull(item.get("Quantity"), 0);
        standard_price = ifnull(item.get("Standard_Price"), 0);

        // Calculate the extended price
        extended_standard_price = quantity * standard_price;
        subform.put("Extended_Standard_Price", extended_standard_price);

        extended_standard_prices.add(subform);
    }

    // Push the updated subform back to CRM
    main_map = Map();
    main_map.put("Quoted_Items", extended_standard_prices);

    update_resp1 = zoho.crm.updateRecord("Quotes", rec.get("id"), main_map);
    info update_resp1;
}

What This Code Does, Line by Line

If you are new to Deluge, here is a plain-English breakdown of what is happening:

  • invokeurl — fetches the full Quote record via a direct API call. The connection parameter refers to a Zoho CRM connection you set up under Setup → Developer Space → Connections.
  • fetch_record.get("data") — the response is a JSON object; the actual records are nested inside a key called data.
  • ifnull(value, 0) — a defensive measure. If a subform cell is empty, ifnull substitutes 0 so your multiplication does not throw an error.
  • subform.put("id", ...) — including the row ID ensures CRM updates the existing row rather than adding a duplicate new one.
  • zoho.crm.updateRecord — pushes the modified subform back to the Quote record.
ⓘ Why use invokeurl here instead of getRecordById? In some Zoho CRM configurations and older API versions, zoho.crm.getRecordById may not return subform data reliably. The invokeurl approach using the CRM REST API v6 is more explicit and consistent, especially for complex subforms with many fields.

6. Common Mistakes and How to Avoid Them

Even with a clear guide in hand, a few recurring mistakes trip developers up when working with Zoho CRM subforms. Here is what to watch out for:

 Forgetting to include existing row IDs
This is the single most common mistake. If you push a List to a subform without including the id of existing rows, Zoho CRM treats every entry as a new row — and silently deletes the old ones. Always fetch the existing rows first and include their IDs in your update List.
 Using field label names instead of API names
If your subform column is labelled "Unit Price" on screen, its API name might be Unit_Price or something entirely different. Always go to CRM Setup → the module → Fields to get the exact API name. Using the display label in your Map keys will result in no data being written.
 Not handling null values with ifnull
If a subform cell is empty and your Deluge code tries to perform a mathematical operation on it, the function will throw an error and stop. Wrap any field you intend to calculate with ifnull(value, 0) to give it a safe default.
 Testing on a live record with important data
Always test your Deluge function on a dummy or sandbox record first. If your code has a bug that sends an incomplete List to the subform, it can delete real data that may not be recoverable. Zoho CRM does not have a built-in undo for subform updates made via code.

7. When to Use This Approach vs. the External API

This guide focuses on doing everything inside Zoho CRM using Deluge — no external API calls from a server or third-party tool. That approach works well in most cases, but it helps to understand when each option is the right fit:

Use Case Deluge (This Guide) External API
Triggered by a CRM workflow ✓ Ideal Possible but complex
Triggered by an external system Not suitable ✓ Ideal
No coding environment needed ✓ Works in CRM console Requires external setup
Bulk update thousands of records May hit rate limits ✓ Better control
Real-time response to CRM events ✓ Native and fast Requires webhook setup

For the majority of day-to-day CRM automation tasks — updating subforms when a record is saved, when a deal stage changes, or when a workflow rule fires — the Deluge approach is simpler, faster to implement, and easier to maintain long-term.


8. Conclusion

Updating subforms in Zoho CRM without losing existing data is one of those things that feels harder than it is — until you understand the pattern. The key insight is simple: Zoho CRM replaces the entire subform on every update, so you must always include the existing rows (by their IDs) alongside any new rows you want to add.

Once that clicks, the four-step pattern becomes second nature. Create your new row as a Map, add it to a List, fetch and append the existing rows by ID, then push the full List back with updateRecord. That is all there is to it.

From here, you can extend this pattern in many directions — conditionally adding rows based on field values, computing and writing calculated fields across an entire subform, or syncing subform data between related records. The foundation is the same in every case.

If you found this guide helpful, consider bookmarking it as a reference the next time you work with Zoho CRM subforms. And if you run into a scenario not covered here, drop a comment below — real-world questions make for the best follow-up articles.

Post a Comment