loader

When you want to push data into the Zoho CRM API or pull data out, you’ve got a few options. My favourite option is to write a standalone custom function in Zoho CRM. What this involves is creating a custom function using Deluge script and exposing it to the internet via Deluge script. The reasons I like it so much are:

  1. The deluge script API wrappers are generally very terse and easy to understand compared to SDK code. I can quickly smash out code to retrieve and update data in the CRM whereas it would take me a lot longer to write all the SDK boilerplate
  2. You don’t have to worry about OAuth. Have a read of the article on using the SDKs to understand why this is a major advantage. Instead of fussing around with refresh tokens and self clients, you’ve got a simple API key you can use.
  3. Serverless functions require no server infrastructure. You don’t really have this option if you’re using the SDKs because you need to persist the OAuth refresh token somewhere.

Code terseness

Maybe the whole OAuth deal doesn’t phase you. I think the verbosity of SDK code will.

Here’s an example of some real world PHP code my team wrote to create a record using the PHP SDK:

public function createSMSRecord($messageType, $messageStatus, $To, $From, $messageSource,
    $twilioWebhookContents, $crmContactField, $Name, $Message, $Sid, $userId, $crmContactFieldValue, $imageUrls, $newMsgDate, $crmModuleName) {

    $refreshToken = ZohoCRM:: getUserMetaData($userId, 'refresh_token');
    $email = User:: where('id', $userId) -> value('email');
    $accountsUrl = ZohoCRM:: getUserMetaData($userId, 'accounts_url');

    $customStoreInitialized = CustomStore:: initialize($refreshToken, $email, $accountsUrl);
    if ($customStoreInitialized) {
        $moduleAPIName = 'twiliosmsextension0__Sent_SMS';
        $recordOperations = new RecordOperations();
        $bodyWrapper = new BodyWrapper();
        $records = array();
        $recordClass = 'com\zoho\crm\api\record\Record';
        $newMessageRecord = new $recordClass();

        $newMessageRecord -> addFieldValue(new Field('Message_Type'), $messageType);
        $newMessageRecord -> addFieldValue(new Field('Sid'), $Sid);
        $newMessageRecord -> addFieldValue(new Field('twiliosmsextension0__Status'), $messageStatus);
        $newMessageRecord -> addFieldValue(new Field('twiliosmsextension0__Message_Source'), $messageSource);
        $newMessageRecord -> addFieldValue(new Field('twiliosmsextension0__Twilio_Webhook_Contents'), json_encode($twilioWebhookContents));
        if ($Message) {
            $newMessageRecord -> addFieldValue(new Field('Message'), $Message);
        }
        if ($imageUrls) {
            $newMessageRecord -> addFieldValue(new Field('twiliosmsextension0__MMS_content'), $imageUrls);
        }
        $newMessageRecord -> addFieldValue(new Field('Message_Date'), $newMsgDate);
        $newMessageRecord -> addFieldValue(new Field('Name'), $Name);
        $newMessageRecord -> addFieldValue(new Field('From'), $From);
        $newMessageRecord -> addFieldValue(new Field('To'), $To);

        //set ContactName/LeadName/DealName or any custom module name field.
        $contactName = new Record();
        $contactName -> setId($crmContactFieldValue);
        $newMessageRecord -> addFieldValue(new Field($crmContactField), $contactName);

        $tagList = array();
        $tag = new Tag();

        $tag -> setName($messageType);

        array_push($tagList, $tag);
        //Set the list to Tags in Record instance
        $newMessageRecord -> setTag($tagList);
        //Add Record instance to the list
        array_push($records, $newMessageRecord);
        //Set the list to Records in BodyWrapper instance
        $bodyWrapper -> setData($records);

        $trigger = array("approval", "workflow", "blueprint");
        $bodyWrapper -> setTrigger($trigger);

        $headerInstance = new HeaderMap();
        //Call createRecords method that takes BodyWrapper instance as parameter.
        $response = $recordOperations -> createRecords($moduleAPIName, $bodyWrapper);
        $result = '';
        if ($response != null) {
            //Get the status code from response
            if ($response -> isExpected()) {
                //Get object from response
                $actionHandler = $response -> getObject();
                if ($actionHandler instanceof ActionWrapper) {
                    //Get the received ActionWrapper instance
                    $actionWrapper = $actionHandler;
                    //Get the list of obtained ActionResponse instances
                    $actionResponses = $actionWrapper -> getData();
                    foreach($actionResponses as $actionResponse) {
                        //Check if the request is successful
                        if ($actionResponse instanceof SuccessResponse) {
                            //Get the received SuccessResponse instance
                            $successResponse = $actionResponse;
                            //Get the Status
                            $successStatus = $successResponse -> getStatus() -> getValue();
                            //Get the Code
                            $successCode = $successResponse -> getCode() -> getValue();

                            if (isset($successStatus) && $successStatus == 'success' && isset($successCode) && $successCode == 'SUCCESS') {
                                $record = $actionResponse -> getDetails();
                                $result = array(
                                    'Status' => $successStatus,
                                    'Message' => $successResponse -> getMessage() -> getValue(),
                                    'CRMRecordId' => $record['id']
                                );
                            }
                        }
                        //Check if the request returned an exception
                        else if ($actionResponse instanceof APIException) {
                            //Get the received APIException instance
                            $exception = $actionResponse;
                            //Get the Status
                            Log:: debug("Status: ".$exception -> getStatus() -> getValue(). "\n");
                            //Get the Code
                            Log:: debug("Code: ".$exception -> getCode() -> getValue(). "\n");
                            //Get the details map
                            foreach($exception -> getDetails() as $key => $value) {
                                //Get each value in the map
                                Log:: debug(["key :".$key, "value :".$value]);
                            }
                            //Get the Message
                            Log:: debug("Message: ".$exception -> getMessage() -> getValue());
                        }
                    }
                }
                //Check if the request returned an exception
                else if ($actionHandler instanceof APIException) {
                    //Get the received APIException instance
                    $exception = $actionHandler;
                    //Get the Status
                    Log:: debug("Status: ".$exception -> getStatus() -> getValue(). "\n");
                    //Get the Code
                    Log:: debug("Code: ".$exception -> getCode() -> getValue(). "\n");
                    //Get the details map
                    foreach($exception -> getDetails() as $key => $value)
                    {
                        //Get each value in the map
                        Log:: debug(["key :".$key, "value :".$value]);
                    }
                    //Get the Message
                    Log:: debug("Message: ".$exception -> getMessage() -> getValue(). "\n");
                }
            }
            else {
                Log:: debug(['response', $response]);
            }
        }
        if ($result != '') {
            return $result;
        }
    }
}

Wowsers. Let me show you what the equivalent deluge script code looks like:

try {
    received_or_sent = if (twilio_api_response.get('direction') == "inbound", "received from ", "sent to ");
    new_twilio_message = Map();
    timestamp = zoho.currentdate.toString("dd-MMM-yyyy");

    new_twilio_message.put("Name", received_or_sent + msg_from_name + " on " + timestamp + "");
    new_twilio_message.put("From", twilio_api_response.get('from'));
    new_twilio_message.put("To", twilio_api_response.get('to'));
    new_twilio_message.put("Message", twilio_api_response.get('body'));
    status = twilio_api_response.get('status');
    if (status == null) {
        status = if (twilio_api_response.get('direction') == "inbound", "received", "");
    }
    new_twilio_message.put("twiliosmsextension0__Status", status.toString());
    new_twilio_message.put("Message", twilio_api_response.get('body'));
    new_twilio_message.put("Message_Source", twilio_api_response.get("Message_Source"));
    hour = zoho.currenttime.toString("yyyy-MM-dd'T'HH");
    new_twilio_message.put("twiliosmsextension0__Hour_Received", hour);
    new_twilio_message.put(lookup_field_name, lookup_id);

    create_resp = zoho.crm.createRecord("twiliosmsextension0__Sent_SMS", new_twilio_message);
    return create_resp.get("id");
}
catch (e) {
    twiliosmsextension0.debug_in_cliq("error creating sms record " + " - " + e + twilio_api_response);
}
return "";

Much much simpler. Gone is all the boilerplate related to checking whether the record was created properly. It either works or it doesn’t (in which case, the catch block will handle it).

Write your own serverless custom function

Hopefully I’ve convinced you that serverless functions are an awesome way to interact with the Zoho CRM API. Here’s a video showing how to create serverless functions and how to integrate with them from the rest of your codebase. They work with both push and pull use cases.