What?
This is a quick bit of code for my reference so that I don't have to keep finding a function that gives me the syntax of how to either create or update a record using invokeUrl.

Why?
Because my designers keep including custom fields in their transactional modules line items (quotes, sales orders, invoices, purchase orders). The only way to update these are by using the API and deluge function invokeURL rather than the usual shortcode of zoho.crm.updateRecord() or zoho.crm.createRecord().

How?
This is somewhere in the official documentation but here's the bits that I need:

Updating a record with PUT (note that id needs passed for each row otherwise you will end up with duplicate line items):

/* *******************************************************************************
	Function:       void automation.fn_Quotes_OnEdit(int p_QuoteID)
	Label:          Fn - Quotes - On Edit
	Trigger:        Used in a workflow when a quote is modified
	Purpose:		Actions when a quote is modified
	Inputs:         int p_QuoteID - the ID of the CRM Quote Record
	Outputs:        void

	Date Created:   2024-12-10 (Joel Lipman)
					- Initial release
	Date Modified:	???
					- ???

	More Information:
					Any information that may help

	******************************************************************************* */
//
// initialize
m_UpdateQuote = Map();
//
// get quote details
v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
r_QuoteDetails = invokeurl
[
	url :v_Endpoint
	type :GET
	connection:"zcrm"
];
l_RecordData = ifnull(r_QuoteDetails.get("data"),List());
for each  m_RecordData in l_RecordData
{
	//
	// apply changes to any of the line items
	l_NewLineItems = List();
	if(!isNull(m_RecordData.get("id")))
	{
		for each  m_LineItem in m_RecordData.get("Quoted_Items")
		{
			m_NewLineItem = Map();
			m_NewLineItem.put("id",m_LineItem.get("id"));
			m_NewLineItem.put("Description",m_LineItem.get("Description"));
			m_NewLineItem.put("Discount",m_LineItem.get("Discount"));
			m_NewLineItem.put("CUSTOM_FIELD_1",m_LineItem.get("CUSTOM_FIELD_1"));
			m_NewLineItem.put("CUSTOM_FIELD_2",m_LineItem.get("CUSTOM_FIELD_2"));
			m_NewLineItem.put("Product_Name",m_LineItem.get("Product_Name"));
			m_NewLineItem.put("Quantity",m_LineItem.get("Quantity"));
			m_NewLineItem.put("List_Price",m_LineItem.get("List_Price"));
			l_NewLineItems.add(m_NewLineItem);
		}
	}
}
//
// if there is reason to update it then let's update CRM API v7 style
if(l_NewLineItems.size() > 0)
{
	// 
	// build request to send to CRM v7
	// m_RecordData should have value from last iteration
	m_UpdateQuote.put("Subject",m_RecordData.get("Subject"));
	m_UpdateQuote.put("Quoted_Items",l_NewLineItems);
	l_RecordsToSend = List();
	l_RecordsToSend.add(m_UpdateQuote);
	m_Data = Map();
	m_Data.put("data",l_RecordsToSend);
	// 
	// this is REST API so by default all triggers run.  Use an empty list to stop or prevent these from triggering. 
	m_Data.put("trigger",List());
	v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
	r_QuoteDetails = invokeurl
	[
		url :v_Endpoint
		type :PUT
		parameters:m_Data.toString()
		connection:"zcrm"
	];
	info r_QuoteDetails;
}


Creating a record
Pretty much the same as above but instead of the method/type PUT you will use POST with an endpoint of https://www.zohoapis.com/crm/v7/Quotes.

Showing Off
Just a note here for myself where using invokeAPI over invokeURL:

// 
// retrieve record details
r_SoDetails = invokeapi
[
	service :zohocrm
	path :"/crm/v7/Sales_Orders/" + p_SalesOrderID
	type :GET
	connection:"zcrm"
];
l_SoDetails = ifnull(r_SoDetails.get("data"),List());
m_SoDetails = if(l_SoDetails.size() > 0,l_SoDetails.get(0),Map());
info "Sales Order Record Details: ";
info m_SoDetails;

Where format/syntax is:

<variable> = invokeapi
[
	service: <service>
	path: <path>
	type: <request_type>
	parameters: <expression>
	connection: <connection_name>
];


Additional Note(s):

  • The trigger input can be workflow, approval, blueprint, pathfinder, or orchestration(for the Journey Builder feature). If "trigger" is not mentioned, the automation actions related to the API will get executed. Enter the trigger value as [] to not execute the workflows. [1]
  • To remove or delete a line item when using invokeURL and the PUT method, submit the line item again with its ID as well as a key called "_delete" as per the following snippet:
    //
    // initialize
    m_UpdateQuote = Map();
    //
    // get quote details
    v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
    r_QuoteDetails = invokeurl
    [
    	url :v_Endpoint
    	type :GET
    	connection:"zcrm"
    ];
    l_RecordData = ifnull(r_QuoteDetails.get("data"),List());
    for each  m_RecordData in l_RecordData
    {
    	//
    	// apply changes to any of the line items
    	l_NewLineItems = List();
    	if(!isNull(m_RecordData.get("id")))
    	{
    		for each  m_LineItem in m_RecordData.get("Quoted_Items")
    		{
    			if(!isNull(m_LineItem.get("id")) && !m_LineItem.get("Optional"))
    			{
    				// add here line items that will be added or updated
    				m_NewLineItem = Map();
    				m_NewLineItem.put("id",m_LineItem.get("id"));
    				m_NewLineItem.put("Description",m_LineItem.get("Description"));
    				m_NewLineItem.put("Discount",m_LineItem.get("Discount"));
    				m_NewLineItem.put("CUSTOM_FIELD_1",m_LineItem.get("CUSTOM_FIELD_1"));
    				m_NewLineItem.put("CUSTOM_FIELD_2",m_LineItem.get("CUSTOM_FIELD_2"));
    				m_NewLineItem.put("Product_Name",m_LineItem.get("Product_Name"));
    				m_NewLineItem.put("Quantity",m_LineItem.get("Quantity"));
    				m_NewLineItem.put("List_Price",m_LineItem.get("List_Price"));
    				l_NewLineItems.add(m_NewLineItem);
    			} 
    			else 
    			{
    				// add here line items to delete / remove / omit
    				m_NewLineItem = Map();
    				m_NewLineItem.put("id",m_LineItem.get("id"));
    				m_NewLineItem.put("_delete",null);
    				l_NewLineItems.add(m_NewLineItem);
    			}
    		}
    	}
    }
    //
    // if there is reason to update it then let's update CRM API v7 style
    if(l_NewLineItems.size() > 0)
    {
    	// 
    	m_UpdateQuote.put("Subject",m_RecordData.get("Subject"));
    	m_UpdateQuote.put("Quoted_Items",l_NewLineItems);
    	l_RecordsToSend = List();
    	l_RecordsToSend.add(m_UpdateQuote);
    	m_Data = Map();
    	m_Data.put("data",l_RecordsToSend);
    	// 
    	// this is REST API so by default all triggers run.  Use an list to specify any triggers on update. Use an empty list to stop any. 
    	m_Data.put("trigger",{"workflow","approval"});
    	v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
    	r_QuoteDetails = invokeurl
    	[
    		url :v_Endpoint
    		type :PUT
    		parameters:m_Data.toString()
    		connection:"zcrm"
    	];
    	info r_QuoteDetails;
    }


Source(s):