How to transfer files from Creator file upload fields to CRM file upload fields

How to transfer files from Creator file upload fields to CRM file upload fields

This article describes how to transfer files from Zoho Creator file upload fields to Zoho CRM file upload fields. I'm posting it here because the current documentation does not fully and accurately describe how to do this with certain file types (PDF, in my case), and I was only able to get it working after a lot of troubleshooting and back-and-forth with Creator support.

If you try to transfer a PDF from a Creator record to a CRM file upload field, you may encounter one the following:

  • The file arrives in CRM blank or corrupted
  • The file size is larger than the original (a 14KB file becoming 23KB is a telltale sign)
  • Every step of your pipeline returns a SUCCESS response, making the bug nearly impossible to locate
  • The documentation describes functions that either don't exist in Creator or explicitly warn that they don't work for binary files
  • A script that works for a CSV file fails for a PDF file

Here's how to make it work.


The Objective

Transfer a file uploaded via a Zoho Creator form field into a file upload field on a Zoho CRM record, preserving the file intact.


The obvious approach is to download the file from Creator using invokeUrl, convert it to a file object using toFile(), upload it to the CRM file store, and then update the CRM record with the resulting file ID. This approach is described in Zoho's documentation and appears to work, as every step returns a success response, but produces a blank or corrupted file in CRM for any binary file type, including PDFs.


The root cause is that toFile() does not faithfully round-trip binary data through Deluge's runtime. For plain text files like CSVs, this isn't a problem. For binary files like PDFs, toFile() corrupts the content during conversion. The size inflation (original file smaller than the CRM version) is the diagnostic signature of this corruption.


The documentation offers several apparent alternatives:

getFileContent() returns TEXT and explicitly warns in a footnote that "file types that usually contain images (for example .pdf, etc.) will return junk values." 

setFileType() is documented as applicable to all Zoho services except Zoho Creator, making it useless in this context.


The Missing Documentation

The solution differs depending on whether you are transferring a text-based file (CSV, TXT) or a binary file (PDF).

For Text-Based Files (CSV, TXT): Use the v5 Endpoint with toFile()

For plain text files, the standard pipeline works correctly. Use toFile() to convert the invokeUrl response, POST to the v5 CRM files endpoint, and update the record using zoho.crm.updateRecord.

  1. fileContent = invokeurl
  2. [
  3.     url :"https://www.zohoapis.com/creator/v2.1/data/<owner>/<app>/report/<ReportName>/<recordId>/<FieldName>/download"
  4.     type :GET
  5.     connection:"creator"
  6. ];
  7. fileName = "contacts.csv";
  8. fileObject = fileContent.toFile(fileName);
  9. fileObject.setParamName("file");
  10. uploadResponse = invokeurl
  11. [
  12.     url :"https://www.zohoapis.com/crm/v5/files"
  13.     type :POST
  14.     files:fileObject
  15.     connection:"z_oauth2"
  16. ];
  17. fileId = uploadResponse.getJSON("data").get(0).getJSON("details").getJSON("id");
  18. fileList = List();
  19. fileList.add(fileId);
  20. updateMap = Map();
  21. updateMap.put("Your_File_Field_API_Name", fileList);
  22. zoho.crm.updateRecord("Your_Module", crmRecordId.toLong(), updateMap, Map(), "zcrm");


For Binary Files (PDF): Use the v8 Endpoint with setParamName()

For binary files, skip toFile() entirely. Call setParamName() directly on the invokeUrl response object, POST to the v8 CRM files endpoint, and update the record using a raw invokeUrl PUT with the File_Id__s map structure. The zoho.crm.updateRecord method does not work for v8 file field updates.

  1. fileContent = invokeurl
  2. [
  3.     url :"https://www.zohoapis.com/creator/v2.1/data/<owner>/<app>/report/<ReportName>/<recordId>/<FieldName>/download"
  4.     type :GET
  5.     connection:"creator"
  6. ];
  7. fileContent.setParamName("file");
  8. uploadResponse = invokeurl
  9. [
  10.     url :"https://www.zohoapis.com/crm/v8/files"
  11.     type :POST
  12.     files:fileContent
  13.     connection:"z_oauth2"
  14. ];
  15. fileId = uploadResponse.getJSON("data").get(0).getJSON("details").getJSON("id");
  16. fileMap = Map();
  17. fileMap.put("File_Id__s", fileId);
  18. fileList = List();
  19. fileList.add(fileMap);
  20. fileUpdateMap = Map();
  21. fileUpdateMap.put("Your_File_Field_API_Name", fileList);
  22. dataList = List();
  23. dataList.add(fileUpdateMap);
  24. payload = Map();
  25. payload.put("data", dataList);
  26. updateResponse = invokeurl
  27. [
  28.     url :"https://www.zohoapis.com/crm/v8/Your_Module_API_Name/" + crmRecordId
  29.     type :PUT
  30.     parameters:payload.toString()
  31.     connection:"zcrm"
  32. ];

Validating File Types Before Submission

Since the two file types require different pipelines, it is worth validating the file extension before the on-submit script runs. In Zoho Creator, alert and cancel submit are only valid in validate scripts, not on-submit scripts. Add a validate script to your form with the following pattern:

  1. if(input.Your_CSV_Field != "")
  2. {
  3.     filePath = input.Your_CSV_Field.toString();
  4.     fileExt = filePath.subString(filePath.lastIndexOf(".") + 1).toLowerCase();
  5.     if(fileExt != "csv")
  6.     {
  7.         alert "This field requires a CSV file. Please remove the current file, attach a CSV file, and resubmit.";
  8.         cancel submit;
  9.     }
  10. }
  11. if(input.Your_PDF_Field != "")
  12. {
  13.     filePath = input.Your_PDF_Field.toString();
  14.     fileExt = filePath.subString(filePath.lastIndexOf(".") + 1).toLowerCase();
  15.     if(fileExt != "pdf")
  16.     {
  17.         alert "This field requires a PDF file. Please remove the current file, attach a PDF file, and resubmit.";
  18.         cancel submit;
  19.     }
  20. }

The .toLowerCase() call on the extension is important -- without it, a file named Report.PDF would pass validation on some systems and fail on others.


Connection Setup

This pipeline requires three connections:

  • creator -- used for the invokeUrl GET to download the file from Creator. This is the built-in Zoho Creator connector in Microservices > Connections
  • z_oauth2 -- used for the invokeUrl POST to upload the file to the CRM file store; requires scopes ZohoCRM.bulk.ALL and ZohoCRM.bulk.READ. This is the OAuth2 connection type.
  • zcrm -- used for the invokeUrl PUT to update the CRM record (v8 PDF pipeline) or zoho.crm.updateRecord (v5 CSV pipeline). This uses the built-in Zoho CRM connection type.

Summary

File Type
Download Method
Upload Endpoint
Update Method
CSV / TXT
invokeUrl + toFile()
v5 /crm/v5/files
zoho.crm.updateRecord
PDF
invokeUrl + setParamName()
v8 /crm/v8/files
invokeUrl PUT with File_Id__s

I was not able to find documentation that explains the distinction between v5 and v8 and the requirement to skip toFile() for binary files in Zoho's official documentation at the time of writing. This pipeline was identified through direct testing and a support escalation with Zoho Creator support.


Hope this saves someone a few hours. If Zoho has updated their documentation to cover this since this was posted, please add a comment.