Deluge
A practical reference for writing Zoho CRM Functions in Deluge.
Table of Contents
1. CRM Integration Tasks
- zoho.crm.v8.createRecord
- zoho.crm.v8.getRecords
- zoho.crm.v8.getRecordById
- zoho.crm.v8.searchRecords
- zoho.crm.v8.updateRecord
- zoho.crm.v8.upsert
- zoho.crm.v8.bulkCreate
- zoho.crm.v8.bulkUpdate
- zoho.crm.v8.getRelatedRecords
- zoho.crm.v8.updateRelatedRecord
- zoho.crm.v8.convertLead
- zoho.crm.v8.attachFile
2. Built-in Zoho Services
3. Zoho Variables
4. Deluge Task Statements
5. Using Connections
6. Logging and Error Handling
7. Data Types
8. Accessing Arguments via the input Map
9. Returning Values per Category
Deluge is Zoho's proprietary scripting language and the most deeply integrated language for writing CRM Functions. It has direct access to zoho.crm.* integration tasks as well as native integration tasks for other Zoho services, and first-class support for argument mapping via merge variables.
This guide covers the CRM-specific integration tasks available in Deluge including how to read and write records, fetch modules and organisation details, read CRM variables, access other Zoho services, handle arguments, and return values. For general Deluge syntax (control flow, loops, string manipulation, collections), refer to the Deluge language documentation.
No draft state:
Deluge Functions do not have a draft/deploy workflow. Every save in the editor immediately updates the live Function. Be cautious when editing Functions that are associated with workflows, buttons, or schedules. Changes take effect immediately when you save them. Java, Node.js, and Python Functions have a separate draft and deploy step. See Java, Node.js, Python Language Guide for details.
CRM Integration Tasks
Deluge provides built-in integration tasks under the zoho.crm namespace for performing record operations, bulk processing, related record handling, lead conversion, and file attachments. These tasks are available directly in every Deluge Function. No SDK setup, imports, or connection configuration is required.
Note:
All tasks below accept an optional connection parameter as the last argument. This is only needed when calling CRM from a different Zoho service. Within a CRM Function, you can omit it.
zoho.crm.v8.createRecord
Creates a new record in the specified module.
Syntax:
response = zoho.crm.v8.createRecord(<module_name>, <record_values>, <options_map>, <connection>);
Example: Creating a Lead
leadinfo = {"Company":"Zylker", "Last_Name":"Williams", "Phone":"+1 678 XXX XXXX", "Email":"will@zylker.com", "Country":"US"};
response = zoho.crm.v8.createRecord("Leads", leadinfo);
info "Created ID: " + response.get("id");
Example: Adding Notes to a Lead
notesMap = {"Parent_Id":"2303XXXXXXXXXX", "Note_Title":"Title", "Note_Content":"Note_Description", "$se_module":"Leads"};
response = zoho.crm.v8.createRecord("Notes", notesMap);
Example: Custom Module
custominfo = {"Name":"John", "Phone":"+1 678 XXX XXXX", "Email":"john@zylker.com"};
response = zoho.crm.v8.createRecord("Hotels", custominfo);
Response format:
Success:
{"Modified_Time":"2018-03-26T14:33:01+05:30", "Modified_By":{"name":"Ben","id":"2303XXXXXXXXXX"}, "Created_Time":"2018-03-26T14:33:01+05:30", "id":"2303XXXXXXXXXX", "Created_By":{"name":"Ben","id":"2303XXXXXXXXXX"}}
Failure:
{"code":"MANDATORY_NOT_FOUND", "details":{"api_name":"Last_Name"}, "message":"required field not found", "status":"error"}
zoho.crm.v8.getRecords
Fetches records from the specified module with pagination and sorting options.
Syntax:
response = zoho.crm.v8.getRecords(<module_name>, <page>, <per_page>, <query_map>, <connection>);
Example: fetch sorted Contacts
query_map = Map();
query_map.put("sort_order", "asc");
query_map.put("sort_by", "First_Name");
response = zoho.crm.v8.getRecords("Contacts", 1, 10, query_map);
Example: Fetch from a Custom Module
response = zoho.crm.v8.getRecords("Hotels", 1, 200, Map());
zoho.crm.v8.getRecordById
Fetches a single record using its module and record ID.
Syntax:
response = zoho.crm.v8.getRecordById(<module_name>, <record_ID>, <query_value>, <connection>);
Example:
response = zoho.crm.v8.getRecordById("Leads", 23033XXXXXXXXXXXXXX);
info response.get("Last_Name");
Tip:
Field names in the response use the field's API name, not the display label. You can find API names in Setup → Developer Hub → APIs → API Names.
zoho.crm.v8.searchRecords
Searches for records in a module matching specific criteria.
Syntax:
response = zoho.crm.v8.searchRecords(<module_name>, <criteria>, <page>, <per_page>, <options_map>, <connection>);
Example: Search by Email
response = zoho.crm.v8.searchRecords("CustomModule1", "(Email:equals:john@zylker.com)");
Example: Search Accounts Starting with a Character
response = zoho.crm.v8.searchRecords("Accounts", "(Account_Name:starts_with:A)");
Common criteria operators:equals, not_equal, greater_than, less_than, starts_with, contains, between.
Note:
searchRecords returns a maximum of 200 records per call. If your query may match more, implement pagination or narrow the criteria.
zoho.crm.v8.updateRecord
Updates a particular record using its ID in the specified module.
Syntax:
response = zoho.crm.v8.updateRecord(<module_name>, <record_ID>, <record_value>, <options_map>, <connection>);
Example:
leadinfo = {"Company":"Zylker", "Last_Name":"Stewart", "Phone":"9876XXXXXX", "Email":"stewart@zylker.com"};
response = zoho.crm.v8.updateRecord("Leads", 23033XXXXXXXXXXXXXX, leadinfo);
Note:
Include only the fields you want to update. Fields not included in the map remain unchanged.
zoho.crm.v8.upsert
Updates a record if the ID is provided; creates a new record if not.
Syntax:
response = zoho.crm.v8.upsert(<module_name>, <record_values>, <options_map>, <connection>);
Example: Create (no ID)
record_values = Map();
record_values.put("Last_Name", "Williams");
record_values.put("Company", "Will Tech");
response = zoho.crm.v8.upsert("Leads", record_values);
Example: Update (with ID)
record_values = Map();
record_values.put("Last_Name", "John");
record_values.put("id", 2303XXXXXXXXXXXXXXX);
response = zoho.crm.v8.upsert("Leads", record_values);
zoho.crm.v8.bulkCreate
Creates multiple records simultaneously. Up to 100 records at a time.
Syntax:
response = zoho.crm.v8.bulkCreate(<module_name>, <record_list>, <options_map>, <connection>);
Example:
createList = List();
createList.add({"Company":"Zylker", "Last_Name":"Daly", "First_Name":"Paul", "Email":"p.daly@zylker.com"});
createList.add({"Company":"Tycoon", "Last_Name":"Richard", "First_Name":"Brian", "Email":"brian@villa.com"});
response = zoho.crm.v8.bulkCreate("Leads", createList);
zoho.crm.v8.bulkUpdate
Updates multiple records simultaneously. Up to 100 records at a time. Each record map must include its id.
Syntax:
response = zoho.crm.v8.bulkUpdate(<module_name>, <record_list>, <options_map>, <connection>);
Example:
updateList = List();
updateList.add({"id":"3171565000000600001", "Company":"Zylker", "Last_Name":"Daly", "Email":"p.daly@zylker.com"});
updateList.add({"id":"3171565000000600002", "Company":"Tycoon", "Last_Name":"Richard", "Email":"brian@villa.com"});
response = zoho.crm.v8.bulkUpdate("Leads", updateList);
zoho.crm.v8.getRelatedRecords
Fetches records from a submodule (related list) related to a record in a parent module.
Syntax:
response = zoho.crm.v8.getRelatedRecords(<submodule_name>, <parent_module>, <parent_record_ID>, <page>, <per_page>, <query_value>, <connection>);
Example: Fetch Notes from a Lead
notesinfo = zoho.crm.v8.getRelatedRecords("Notes", "Leads", 23033XXXXXXXXXXXXXX);
Example: Fetch Events from a Custom Module with Pagination
eventsinfo = zoho.crm.v8.getRelatedRecords("Events", "Hotels", 23033XXXXXXXXXXXXXX, 1, 3);
zoho.crm.v8.updateRelatedRecord
Updates a record in a submodule related to a record in a parent module.
Syntax:
response = zoho.crm.v8.updateRelatedRecord(<submodule_name>, <submodule_record_ID>, <parent_module>, <parent_record_ID>, <record_value>, <connection>);
Example:
response = zoho.crm.v8.updateRelatedRecord("Price_Books", 2303XXXXXXXXXXXXXXX, "Products", 4770XXXXXXXXXXXXXXX, {"list_price":200});
zoho.crm.v8.convertLead
Converts a lead into a contact, deal, and account.
Syntax:
response = zoho.crm.v8.convertLead(<lead_id>, <values>, <connection>);
Example: Create New Records in Contacts, Deals, and Accounts
deal_values = Map();
deal_values.put("Deal_Name", "Jake");
deal_values.put("Closing_Date", "2018-12-06");
deal_values.put("Stage", "Closed Won");
response = zoho.crm.v8.convertLead(2303XXXXXXXXXXXXXXX, {"Deals":deal_values});
Example: Associate to Existing Account and
values_map = Map();
values_map.put("overwrite", true);
values_map.put("Accounts", "2303XXXXXXXXXXXXXXX");
values_map.put("Contacts", "2303XXXXXXXXXXXXXXX");
response = zoho.crm.v8.convertLead(2303XXXXXXXXXXXXXXX, values_map);
zoho.crm.v8.attachFile
Attaches a file to the specified record.
Syntax:
response = zoho.crm.v8.attachFile(<module_name>, <record_id>, <file_object>, <connection>);
Example:
file_object = invokeUrl[url:"https://assets.example.com/report.pdf" type:GET];
response = zoho.crm.v8.attachFile("Leads", 4770XXXXXXXXXX, file_object);
Note:
Before you can process a file, fetch it using invokeurl or invokeAPI. Deluge cannot operate on local or offline files.
Zoho Service Built-ins
Beyond CRM, Deluge has built-in integration tasks for interacting with other Zoho services directly. Following are a few examples:
| Namespace | Service | Common Operations |
|---|---|---|
| zoho.mail | Zoho Mail | Send emails with subject, body, attachments |
| zoho.books | Zoho Books | Create invoices, fetch payments, manage contacts |
| zoho.invoice | Zoho Invoice | Create and manage invoices |
| zoho.desk | Zoho Desk | Create tickets, update ticket status, add comments |
| zoho.people | Zoho People | Access employee records, manage leave requests |
| zoho.projects | Zoho Projects | Create tasks, update milestones, log time entries |
| zoho.calendar | Zoho Calendar | Create events, manage calendars |
Example: Creating a Zoho Books Invoice from a Deal
invoiceData = Map();
invoiceData.put("customer_id", customerId);
invoiceData.put("line_items", lineItems);
response = zoho.books.createRecord("Invoices", orgId, invoiceData);
info "Invoice created: " + response;
For more details about integration tasks, refer to the Deluge Integration Guide.
Note:
For calling external third-party APIs (non-Zoho), use invokeurl with a configured Connection. See Using Connections below and Connections for details.
Zoho Variables
Deluge exposes a set of system-level variables under the zoho namespace. These are read-only values that are resolved at runtime. They provide information about the current user, organization, and execution environment without requiring an API call.
| Variable | Type | Description |
|---|---|---|
| zoho.currentdate | Date | The current date in the org's timezone |
| zoho.currenttime | Date-Time | The current date and time in the org's timezone |
| zoho.loginuserid | Long | The record ID of the user who triggered the Function |
| zoho.loginuser | String | The email address of the user who triggered the Function |
| zoho.adminuserid | Long | The record ID of the CRM org's super admin |
| zoho.adminuser | String | The email address of the CRM org's super admin |
| zoho.ipaddress | String | The IP address of the user who triggered the Function |
Example: Stamping an Audit Trail on Record Update
updateMap = Map();
updateMap.put("Last_Modified_By_Function", zoho.loginuser);
updateMap.put("Last_Function_Run", zoho.currenttime.toString("yyyy-MM-dd HH:mm:ss"));
zoho.crm.v8.updateRecord("Deals", dealId.toLong(), updateMap);
Example: Conditional Logic based on the Executing User
if (zoho.loginuserid == zoho.adminuserid)
{
info "Executed by admin — full access.";
}
else
{
info "Executed by: " + zoho.loginuser;
}
Date formatting:zoho.currentdate and zoho.currenttime support .toString("<pattern>") for formatting — e.g. zoho.currentdate.toString("dd-MMM-yyyy") produces 05-Mar-2026.
Tasks
Deluge provides built-in task statements for sending emails and making HTTP calls. These are standalone language constructs, not methods that belong to a namespace.
sendmail
Sends an email from the CRM org. Commonly used in Automation and Button Functions to notify contacts, owners, or external recipients.
Syntax:
sendmail
[
from: <userId or email>
to: <recipientEmail>
subject: <subjectString>
message: <bodyString>
]
Parameters:
| Parameter | Required | Description |
|---|---|---|
| from | Yes | The sender email address. Use zoho.loginuserid |
| to | Yes | Recipient email address (String) |
| subject | Yes | Email subject line |
| message | Yes | Email body (supports HTML) |
| cc | No | CC recipients (comma-separated String) |
| bcc | No | BCC recipients |
Example: Notify the Deal Owner when a Deal is Won
ownerEmail = deal.get("Owner").get("email");
sendmail
[
from: zoho.loginuserid
to: ownerEmail
subject: "Deal Won: " + deal.get("Deal_Name")
message: "<h3>Congratulations!</h3><p>Your deal <b>" + deal.get("Deal_Name") + "</b> worth $" + deal.get("Amount") + " has been marked as Closed Won.</p>"
]
Note:
sendmail uses the org's email sending quota. For high-volume or transactional emails, consider using an external service via invokeurl.
invokeurl
Sends an HTTP request to any URL, including external APIs, webhooks, and internal services. This is the primary mechanism for third-party integrations in Deluge.
Syntax:
response = invokeurl
[
url: <endpointURL>
type: <GET | POST | PUT | PATCH | DELETE>
parameters: <bodyMap or bodyString>
headers: <headersMap>
connection: <connectionLinkName>
content-type: <mimeType>
]
Parameters:
| Parameter | Required | Description |
|---|---|---|
| url | Yes | The target endpoint URL |
| type | Yes | HTTP method: GET, POST, PUT, PATCH, DELETE |
| parameters | No | Request body, a Map (auto-serialised) or raw String |
| headers | No | A Map of HTTP header key-value pairs |
| connection | No | The link name of a configured Connection (for OAuth2 / API key auth). See Using Connections. |
| content-type | No | MIME type of the request body (e.g. application/json, application/x-www-form-urlencoded) |
Returns: The response body as a String. If the response is JSON, Deluge auto-parses it into a Map or List.
Example: POST JSON to an External Webhook
payload = Map();
payload.put("event", "deal_closed");
payload.put("deal_name", deal.get("Deal_Name"));
payload.put("amount", deal.get("Amount"));
response = invokeurl
[
url: "https://example.com/crm-events"
type: POST
parameters: payload.toString()
content-type: "application/json"
];
info response;
Example: GET with Headers and a Connection
headers = Map();
headers.put("Accept", "application/json");
response = invokeurl
[
url: "https://api.example.com/v2/contacts?email=" + contactEmail
type: GET
headers: headers
connection: "example_api"
];
info response.get("name");
Tip:
When calling APIs that require authentication, always use a Connection rather than hardcoding API keys in headers. See Using Connections.
invokeAPI
The invokeAPI task is an HTTP client that allows you to access and modify data across Zoho services using their APIs. Unlike invokeurl, where you specify the complete URL, invokeAPI requires only the API path. The appropriate Zoho service domain is resolved automatically based on the organization’s data center, ensuring the request works across all supported data centers.
Note:
Each invokeAPI execution triggers an API call in the back-end. These calls are deducted from your overall external calls limit. The system counts actual executions, not the number of times the task appears in the script.
Syntax:
response = invokeapi
[
service: <zohoServiceName>
path: <apiPath>
type: <GET | POST | PUT | PATCH | DELETE | OPTIONS>
parameters: <queryParams>
body: <requestBody>
headers: <headersMap>
connection: <connectionLinkName>
response-format: <FILE | COLLECTION | STRING | NONE | DEFAULT>
response-decoding: <encodingScheme>
];
Parameters:
| Parameter | Required | Type | Description |
|---|---|---|---|
| service | Yes | Text | The Zoho service name. Allowed values: zohocrm, zohobooks, zohoinvoice, zohobilling, zohoinventory, zohobookings, zohocreator, zohoworkdrive |
| path | Yes | Text | The URL path excluding the base URL. For example, if the full URL is https://www.zohoapis.com/crm/v8/Leads, the path is /crm/v8/Leads |
| type | No | Constant | HTTP method: GET, POST, PUT, PATCH, DELETE, OPTIONS. Default: GET |
| parameters | No | Text / Key-Value | Query strings appended to the URL for filtering or processing |
| body | No | Text / File / Key-Value | The request body. Supports text/plain, application/x-www-form-urlencoded, multipart/form-data, application/json, application/octet-stream |
| headers | No | Key-Value | HTTP header key-value pairs including content-type |
| connection | No | Text | The connection link name. Optional when calling within the same service (e.g. calling CRM APIs from a CRM Function). Required when calling a different Zoho service (e.g. calling Zoho Books from CRM) or when you need specific scopes. Customers can still use a connection even within the same service if they need fine-grained scope control. |
| response-format | No | Text | Return type: FILE, COLLECTION, STRING, NONE, DEFAULT. Default: DEFAULT. Set to NONE to discard the response |
| response-decoding | No | Text | Character encoding for decoding the response. Default: UTF-8 |
Response helpers:
| Method | Description |
|---|---|
| response.status | Returns the HTTP status code (e.g. 200, 400) |
| response.headers | Returns the response headers as a Map |
Timeouts: The task throws a "socket timeout error" if the API takes more than 40 seconds to respond. The default connection timeout is 10 seconds, and the connection request timeout is 5 seconds.
invokeAPI vs invokeurl
| Aspect | invokeAPI | invokeurl |
|---|---|---|
| Target | Zoho services only | Any URL (external or Zoho) |
| URL handling | Path only (domain resolved by data center) | Full URL required |
| Data center aware | Yes (automatically routes to correct DC) | No (you must use the correct domain) |
| Use when | Calling Zoho APIs (CRM, Books, Inventory, etc.) | Calling external third-party APIs |
Sending Body Data
The body parameter supports different formats depending on the content type:
JSON body: Convert to Text using toString()
data = Map();
data.put("Last_Name", "Smith");
data.put("Company", "Acme Corp");
data.put("Email", "smith@acme.com");
response = invokeapi
[
service: zohocrm
path: "/crm/v8/Leads"
type: POST
body: data.toString()
headers: {"content-type": "application/json"}
];
File upload: Use the param-name / param-value format
response = invokeapi
[
service: zohocrm
path: "/crm/v8/Leads/" + recordId + "/Attachments"
type: POST
body: {{"param-name": "file", "param-value": fileVariable}}
];
Key-Value body: For application/x-www-form-urlencoded or multipart/form-data
// Simple key-value (text)
bodyData = {{"param-name": "field_name", "param-value": "field_value"}};
// With content-type and encoding
bodyData = {{"param-name": "field_name", "param-value": "field_value", "content-type": "text/plain", "encoding-type": "UTF-8"}};
// List of key-value pairs
bodyList = List();
bodyList.add({"param-name": "name", "param-value": "Hard Drive"});
bodyList.add({"param-name": "file", "param-value": fileObject});
Note:
If content-type and encoding-type are not specified in key-value body data, the defaults are text/plain and UTF-8 respectively.
Example: Fetch Leads from CRM
response = invokeapi
[
service: zohocrm
path: "/crm/v8/Leads"
type: GET
];
info response;
Since the Function runs within Zoho CRM, no connection is required. The task authenticates automatically using the current CRM session.
Example: Update a CRM Record with JSON Body
updateData = Map();
updateData.put("Stage", "Closed Won");
updateData.put("Closing_Date", zoho.currentdate.toString("yyyy-MM-dd"));
response = invokeapi
[
service: zohocrm
path: "/crm/v8/Deals/" + dealId
type: PUT
body: updateData.toString()
headers: {"content-type": "application/json"}
];
info "Status: " + response.status;
Example: Upload an Attachment to a CRM Record
// Fetch the file
attachment = invokeurl
[
url: "https://example.com/files/report.xls"
type: GET
];
recordId = "2689962xxxxx509150";
response = invokeapi
[
service: zohocrm
path: "/crm/v8/Leads/" + recordId + "/Attachments"
type: POST
body: {{"param-name": "file", "param-value": attachment}}
];
info response.status;
Example: Using a Connection (Optional for Same-Service Calls)
When you need specific OAuth scopes or want explicit scope control, you can pass a connection even for CRM-to-CRM calls:
response = invokeapi
[
service: zohocrm
path: "/crm/v8/Deals"
type: GET
connection: "zohocrm_connection"
];
info response;
Tip:
Use invokeAPI when calling Zoho services. It automatically resolves the appropriate service domain based on the organization’s data center. Use invokeurl for external third-party APIs. For calling other Zoho services (Books, Desk, etc.) from CRM, a connection is required. See Using Connections for setup details.
Using Connections
Connections store OAuth2 tokens or API keys securely within Zoho CRM. When you use a Connection with invokeurl, invokeAPI, Integration Tasks, etc. Deluge automatically adds the required authorization headers, so you do not need to handle access tokens in your code.
Creating a Connection
Connections are configured in Setup → Developer Space → Connections. Each Connection has a unique link name that you use to reference it in your code.
There are three types:
| Type | Use Case | How Auth is Handled |
|---|---|---|
| Zoho OAuth | Calling Zoho APIs (CRM v2 REST API, other Zoho products) | Zoho handles token refresh automatically |
| Custom OAuth2 | External APIs with OAuth2 (Google, Slack, etc.) | You provide client ID, secret, auth/token URLs; Zoho manages refresh |
| API Key / Basic Auth | External APIs with static credentials | You provide the key; Zoho passes it in headers |
Using a Connection in invokeurl
Reference the Connection by its link name in the connection parameter:
response = invokeurl
[
url: "https://www.exampleapis.com/calendar/v3/calendars/primary/events"
type: GET
connection: "example_calendar"
];
info response;
Deluge resolves the Connection at runtime, attaches the appropriate Authorization header, and handles token refresh for OAuth2 Connections transparently.
Example: Creating a to-do Card via OAuth2 Connection
cardData = Map();
cardData.put("name", deal.get("Deal_Name") + " — Follow Up");
cardData.put("idList", "60f1a2b3c4d5e6f7a8b9c0d1");
response = invokeurl
[
url: "https://api.example.com/1/cards"
type: POST
parameters: cardData.toString()
content-type: "application/json"
connection: "trello_oauth"
];
info "Created card: " + response.get("shortUrl");
Logging and Error Handling
info()
info is the primary logging statement in Deluge. It writes output to the Logs panel in the Function Editor and to the execution history. Use it to trace values, debug logic, and confirm execution flow.
Syntax:
info <expression>;
Examples:
info "Function started";
info "Deal Name: " + deal.get("Deal_Name");
info deal; // logs the entire map
info "Record count: " + records.size();
info accepts any data type, including Strings, numbers, Maps, Lists, Booleans. Maps and Lists are printed in their string representation.
Tip:
In production Functions, keep info statements targeted. Excessive logging can make the Logs panel hard to read and may slow execution marginally. Consider removing or commenting out debug info statements before deploying.
try / catch
Deluge supports try - catch blocks for handling runtime errors, such as failed API calls, null values, type mismatches, and other exceptions.
The catch block accepts an exception variable (e), that stores information about the runtime error. You can use this variable to access details such as the line number where the error occurred (e.lineNo) and the error message (e.message). Printing e displays both values together.
Syntax:
try
{
// code that may fail
}
catch (e)
{
// handle the error
info "Error: " + e;
}
Example: Handling a Failed External API Call
try
{
response = invokeurl
[
url: "https://api.example.com/data"
type: GET
connection: "example_api"
];
info "API response: " + response;
}
catch (e)
{
info "API call failed: " + e;
// Optionally update a record with the error status
errorMap = Map();
errorMap.put("Integration_Status", "Failed");
errorMap.put("Error_Message", e.toString());
zoho.crm.v8.updateRecord("Deals", dealId.toLong(), errorMap);
}
Example: Guarding against Null Values
try
{
contactEmail = deal.get("Contact_Name").get("Email");
info "Contact email: " + contactEmail;
}
catch (e)
{
info "No contact linked to this deal — skipping email.";
}
throw
Use throw to explicitly raise an error and halt execution. This is useful in Validation Rule Functions or when a precondition fails.
Syntax:
throw "<errorMessage>";
Example: Validation Before Proceeding
if (deal.get("Amount").toDecimal() <= 0)
{
throw "Deal amount must be greater than zero.";
}
When throw is executed inside a try block, the catch block handles it. Outside a try block, it terminates the Function and the error appears in the execution logs.
Note:
In Workflow-triggered (Automation) Functions, an unhandled throw causes the Function to fail silently from the end user's perspective. The error is recorded only in the execution logs. In Button Functions, the thrown message may surface in the UI response depending on how the return is structured.
Data Types
Deluge is a dynamically typed language, so you do not need to declare types for local variables. However, Function signatures require explicit type annotations for arguments and return values.
| Type | Description | CRM Field Mapping |
|---|---|---|
| Text | Sequence of characters enclosed in double quotes | Name, Email, Single Line, Multi Line |
| Number | Integer values (no decimals). Operations with decimals produce a Decimal result | Number, Auto Number |
| Decimal | Decimal values for currency and percentages | Currency, Percentage |
| Boolean | true or false (not enclosed in quotes, not case-sensitive) | Checkbox (Decision Box) |
| Date-Time | Date and time values enclosed in single quotes. Date without time defaults hours/mins/seconds to 0 | Date, Date-Time, Prediction |
| Time | Time values in 12-hour or 24-hour format, independent of date | Time |
| List | Ordered collection of elements of any type, accessed by index | Multiselect, Checkbox |
| Key-Value | Collection of unique key-value pairs. Duplicate keys overwrite the previous value | CRM API responses (zoho.crm.getRecord returns Key-Value) |
| Collection | Can act as either a List or a Key-Value, but not both at the same time | — |
| File | File data fetched from the web or cloud service via invokeurl / invokeAPI | Attachments |
Note:
null is a built-in constant with no specific value and does not belong to any data type.
CRM Context: Why Key-Value and List matter
CRM API responses (zoho.crm.getRecord, zoho.crm.v8.searchRecords, etc.) return Key-Value maps and Lists of maps. Most CRM Function logic revolves around reading fields from these maps and building maps to create or update records:
// getRecord returns a Key-Value map
deal = zoho.crm.getRecord("Deals", dealId);
info deal.get("Deal_Name");
// searchRecords returns a List of Key-Value maps
leads = zoho.crm.v8.searchRecords("Leads", "(Company:equals:Acme)");
for each lead in leads
{
info lead.get("Last_Name");
}
// Building a Key-Value map for record creation
data = Map();
data.put("Last_Name", "Smith");
data.put("Company", "Acme Corp");
zoho.crm.v8.createRecord("Leads", data);
For the complete data type reference, built-in methods, and collection operations, refer to the Deluge Data Types documentation.
Accessing Arguments via the input Map
In Deluge, arguments declared in the Function (via the Arguments panel or the code signature) are accessible inside the script via the input map.
How it works:
- Declare Function arguments either in the Arguments panel or directly in the Function signature:
void automation.stamp_closure_summary(string dealId, string dealName, string amount)
- When the Function is triggered (by a Workflow Rule, Button, etc.), the trigger resolves each argument from the configured merge variable mapping (e.g. dealId ← ${Deals.Deals Id}).
- Inside the script, arguments are available by name directly:
info dealId; // the value mapped from ${Deals.Deals Id}
info dealName; // the value mapped from ${Deals.Deal Name}
info amount; // the value mapped from ${Deals.Amount}
- Alternatively, you can access arguments via the input map:
info input.dealId;
info input.dealName;
Argument types: String, Integer, Boolean, Map, List. Workflow Rule merge variables always pass values as Strings. Convert them to the required data type within the Function, if needed (e.g. amount.toLong(), amount.toDecimal()).
Important:
In Deluge, Function arguments are not passed automatically. You must declare them and map them when associating the Function with its trigger. This is different from Java, Node.js, and Python where trigger context is delivered automatically via basicIO. See Java, Node.js, Python Language Guide for that approach.
Returning Values per Category
A Deluge Function’s return type is determined by its category and declared in the Function signature. The value you return, and how Zoho CRM processes it, depends on the Function category.
| Category | Return Type | What to Return |
|---|---|---|
| Automation | void | Nothing. The Function performs side effects only. |
| Button | string | A message String displayed as a modal in the CRM UI. |
| Schedule | void | Nothing. The Function performs batch processing silently. |
| Standalone | string | A String value. If exposed as REST API, use crmAPIResponse for full HTTP control. |
| Related List | string | An XML string that CRM parses and renders as a related list. |
| Signals | string | A value forwarded to the Signals API to raise a notification. |
| Validation Rule | map | A Map with pass/fail status and error message. |
Example: Button Function Returning a Confirmation Message
string button.sync_to_erp(string dealId, string dealName)
{
// ... sync logic here ...
return "Deal '" + dealName + "' synced to ERP successfully.";
}
Example: Automation Function (void return)
void automation.stamp_closure_summary(string dealId, string dealName, string amount)
{
summary = "Deal " + dealName + " closed for $" + amount;
updateMap = Map();
updateMap.put("Closure_Summary", summary);
zoho.crm.v8.updateRecord("Deals", dealId.toLong(), updateMap);
info "Updated closure summary.";
}
What's Next?
- For the Java, Node.js, and Python approach (SDKs, basicIO, context variables), check the Java, Node.js, Pythonlanguage guide.
- For argument mapping walkthroughs with Workflow Rules, Buttons, and Blueprints, see Associating Functions with CRM.
- To learn more about other built-in namespaces, refer to the Built-in Functions help page.
- For details on exposing functions through REST APIs, check out the Serverless Functions help page.