Exposing Functions as REST APIs

A guide for turning a Standalone Function into a REST endpoint.

 

Any Standalone Function in Zoho CRM can be exposed as a REST endpoint, making it callable by external applications, partner systems, AI agents, or integration platforms via HTTP. REST API endpoint support is built into the Function. Enable it from the Function's overview tab in the glance view instead of creating a separate endpoint service.

Recommended category:

While any category of Function can technically be exposed, use the Standalone category for REST API endpoints. Standalone Functions are well suited for REST API endpoints because they support handling incoming requests through crmAPIRequest and constructing responses through crmAPIResponse. This makes it easier to process request data, headers, parameters, and authentication details. and return the appropriate response to the API caller.

For a step-by-step setup guide, see Create a Serverless Function.

Enabling the REST API on a Function

  1. Open the Function from Setup -> Developer Hub -> Functions.
  2. Choose the required functions to open the glance view. In the Overview tab go to REST API section.
  3. Choose an authentication method (see below).
  4. CRM generates a unique endpoint URL for the Function.
  5. External systems call the Function by sending HTTP requests to this URL.

The endpoint URL is unique per Function and follows Zoho's API domain pattern. Once enabled, the Function can receive HTTP requests and return HTTP responses, allowing it to serve as a lightweight API layer for your CRM logic.

Authentication Options

API Key (No Authentication)

The endpoint URL serves as the authentication key. Anyone with the URL can invoke the Function, and no additional token or credential exchange is required.

Best for: Inbound webhooks from third-party services, public data feeds, or trusted internal systems.

Security note:

REST API endpoints support OAuth 2.0 and API key authentication, which can be enabled or disabled independently. For production integrations, OAuth 2.0 is recommended. If you use an API key, treat it as a secret and avoid exposing it in client-side code or public repositories. For details on authentication methods, endpoint security, and API key management, refer to Serverless Endpoint Security.

OAuth 2.0 (Authenticated Access)

Requires the caller to obtain an access token via the standard OAuth 2.0 flow. The token is passed as a Bearer token in the Authorization header on every request.

Best for: Partner integrations, user-scoped access, or any scenario where you need to verify the identity of the caller.

Setup steps:

  1. Register your client application in the Zoho API Console to obtain a Client ID and Client Secret.
  2. Complete the OAuth 2.0 authorization flow to obtain an access token.
  3. Pass the token in the Authorization: Zoho-oauthtoken <token> header when calling the endpoint.

Note:

Access tokens expire and must be refreshed. See Connections and Credentials for detailed OAuth 2.0 setup instructions.

Quick Comparison

AspectAPI KeyOAuth 2.0
Authentication requiredNoYes
Best forWebhooks, trusted internal callsPartner integrations, user-scoped access
Caller identityAnonymousAuthenticated user or app
Token expiryNone (URL-based)Access token expires — refresh required

Handling Requests

When a Function is exposed as a REST endpoint, Zoho provides the crmAPIRequest object that gives the Function access to the incoming HTTP request context. This object is available automatically — no manual argument declaration or mapping is required.

crmAPIRequest contains:

PropertyTypeDescription
methodStringThe HTTP method used for the request (GET, POST, PUT, DELETE)
headersObjectHTTP headers sent with the request (e.g. Content-Type, custom headers)
paramsObjectQuery parameters from the URL (e.g. ?module=Deals&limit=10)
bodyStringThe raw request body (typically JSON). Parse it to extract the data sent by the caller
auth_typeStringAuthentication type used for the request
file_contentFile content, when the request includes a file upload
user_infoObjectInformation about the authenticated user which includes name, email, id, zuid, time_zone, and nested org_info (org name, ID, time zone)
recordObjectThe CRM record context, if applicable

Deluge vs Java, Node.js, Python

In Deluge, all properties are accessed directly from crmAPIRequest:

 

info crmAPIRequest.get("method");
info crmAPIRequest.get("user_info").get("email");
info crmAPIRequest.get("params");

In Java, Node.js, and Python, CRM intercepts the crmAPIRequest and distributes its contents across separate basicIO parameters:

crmAPIRequest propertybasicIO parameter
recordExtracted into records
user_infoExtracted into user and organization (org_info)
method, headers, params, body, auth_type, file_contentRemain in the request parameter

This means Java, Node.js, and Python access record data and user info as top-level basicIO parameters, while the HTTP request details are read from basicIO.getParameter("request"). See Java, Node.js, Python Language Guide for details.

Structuring Responses

When a Function is exposed as a REST endpoint, use crmAPIResponse to control the HTTP response sent back to the caller. It is available in all supported languages such as Deluge, Java, Node.js, and Python.

Response Keys

crmAPIResponse is a map/object with 4 keys:

KeyTypeDefaultDescription
status_codeInteger200HTTP status code
bodyString"" (empty)Response body content
headersMap / Object{"Content-Disposition": "attachment;filename=response.json"}Custom response headers
content-typeStringapplication/json;charset=utf-8Response content type

Deluge

In Deluge, crmAPIResponse is a custom Map. Build the map with the keys above and return it directly:

 

body = Map();
body.put("status","success");

crm_api_response = Map();
crm_api_response.put("status_code",200);
crm_api_response.put("body",body);
crm_api_response.put("content-type", "application/json;charset=utf-8");
crm_api_response.put("headers", {"X-Custom-Header": "value"});

return {"crmAPIResponse":crm_api_response};

Java, Node.js, Python

In Java, Node.js, and Python, the response map is passed under the "crmAPIResponse" key inside basicIO.write():

Java:

 

JSONObject body = new JSONObject();
body.put("status", "success");

JSONObject crm_api_response = new JSONObject();
crm_api_response.put("status_code", 200);
crm_api_response.put("body", body.toString());
crm_api_response.put("content-type", "application/json;charset=utf-8");
crm_api_response.put("headers", new JSONObject().put("X-Custom-Header", "value"));

basicIO.write(new JSONObject().put("crmAPIResponse", crm_api_response));

Node.js:

 

var body = '{ "status": "success" }';
var crm_api_response = {
    status_code: 200,
    body: body,
    'content-type': 'application/json;charset=utf-8',
    headers: {
        'X-Custom-Header': 'value'
    }
}
basicIO.write({
    crmAPIResponse: crm_api_response
});

Python:

 

body = '{"status": "success"}'
crm_api_response = {
    "status_code": 200,
    "body": body,
    "content-type": "application/json;charset=utf-8",
    "headers": {
        "X-Custom-Header": "value"
    }
}
basicIO.write({
    "crmAPIResponse": crm_api_response
})

If you do not use crmAPIResponse, the endpoint returns the Function's default return value as a plain string with the default status code (200) and content type.

Type mismatch error:

Each key must match its expected type (e.g. status_code must be an Integer, not a String). If a key has the wrong type, CRM returns a PATTERN_NOT_MATCHED error:

 

{
    "code": "PATTERN_NOT_MATCHED",
    "details": {"api_name": "status_code"},
    "message": "pattern not matched",
    "status": "error"
}

When to use crmAPIResponse:

Use it when you need to set specific status codes (e.g. 201, 400, 404), custom headers, or non-default content types.

Rate Limits and Throttling

REST API endpoints backed by Functions are subject to Zoho CRM's standard API rate limits. These limits apply per org and are shared across all API activity (not just Function endpoints).

Key limits to be aware of:

  • API calls per day varies by CRM edition (Enterprise, Ultimate, etc.). Function endpoint invocations count toward this daily limit.
  • Concurrent executions has a limit on how many Functions can execute simultaneously in an org.
  • Execution time of each Function invocation has a maximum execution time before it is terminated.

For specific numbers, see Platform Limits and Quotas.

Handling limit exhaustion:

When rate limits are exceeded, the endpoint returns an HTTP error response. Design your application to handle rate limit errors gracefully. Retry failed requests using exponential backoff, or spread requests over time to reduce the likelihood of exceeding rate limits.

Testing Endpoints

Zoho CRM does not provide a built-in API testing client for Function endpoints. Use external tools to test your endpoints:

  • curl provides quick command-line testing for both API Key and OAuth endpoints.
  • Postman helps you with visual API testing with request history and environment variables.
  • Your application's HTTP client can test the integration directly from the consuming system.

Tip:

Test the Function logic in the editor first (using the Run button with hardcoded values), then test the endpoint externally to verify the full HTTP request/response cycle including authentication, crmAPIRequest parsing, and crmAPIResponse formatting.