
Here's a question that came up in our developer forum:
"I'm working on a piece of software to automate conversion of Leads into Deals based on order status from my company's website. There are some cases where a previously deleted Deal needs to be restored, such as when a failed order is manually corrected. Recovering a record from the recycle bin manually is trivial, but obviously I'm trying to automate the whole process. Is it possible to recover a record from the recycle bin using the API?"
This is a problem that many developers may have run into when building integrations.
Consider Zylker, a company that syncs orders from their e-commerce platform directly into Zoho CRM as Deals. Their workflow is straightforward: a new order comes in, a Deal is created. An order fails, the Deal gets deleted. Clean and simple.
But real-world workflows rarely stay that simple. Sometimes a failed order gets manually corrected on the e-commerce side. The customer fixes their payment, the warehouse confirms stock, the ops team overrides the rejection. When that happens, Zylker needs to bring that Deal back to life in CRM, complete with all its history, notes, activities, and associations.
The naive solution is to create a new Deal. But that means losing everything that was attached to the original record like call logs, emails, notes, contact role associations. For a sales team, that history is valuable. Creating a duplicate isn't a restore, it's a data integrity problem waiting to happen.
The right solution is to restore the original Deal from the Recycle Bin programmatically. This is the problem we’ll address in this post.
What the Recycle Bin API offers
To implement this workflow, we’ll rely on the Recycle Bin APIs available in Zoho CRM. There are six key endpoints:
Endpoint: GET {api-domain}/crm/v8/settings/recycle_bin
This endpoint fetches all deleted records across modules. In Zylker's case, this is how we search for a deleted Deal before restoring it. The API supports a filters parameter to narrow results based on module, display name, deletion time, or user.
Other supported parameters are:
ids - retrieve specific records by their unique IDs. Takes precedence over filters if both are provided
sort_by - sort by display_name, deleted_time (default), or deleted_by
sort_order - asc or desc
page / per_page - paginate through results. Maximum 200 records per page. Check "more_records": true in the response to know if more pages exist
2. Get a single Recycle Bin record
Endpoint: GET {api-domain}/crm/v8/settings/recycle_bin/{record_id}
This API fetches details of a specific deleted record by its ID.
This is most useful in integrations where the external system stores the CRM record ID at the time of creation.
The Recycle Bin API only returns metadata about deleted records, not the full record details. The response contains the record's id, display_name, module, owner, deleted_by, and deleted_time. If you need the full record details, you should restore the record first and then fetch it via the get records API.3. Restore Recycle Bin records
Endpoint: POST {api-domain}/crm/v8/settings/recycle_bin/{record_id}/actions/restore
This API restores a specific deleted record by its ID. When a record is restored, any associated records that are also in the Recycle Bin, such as Notes, Attachments, or Activities, are restored automatically along with it. This is a key advantage over recreating the record from scratch, where all that history would be permanently lost.
Note: Records are only available in the Recycle Bin for 60 days after deletion. After that they are permanently deleted and cannot be restored via API or manually.
4. Get Recycle Bin record count
Endpoint : GET {api-domain}/crm/v8/settings/recycle_bin/actions/count
This API returns the total number of records currently present in the Recycle Bin.
For example, Zylker could run a nightly process that checks this endpoint first. If the count is zero, the job exits without making additional API calls.
Monitoring this count can also help detect unexpected spikes in deleted records, which may indicate faulty automation or synchronization issues.
Endpoints:
DELETE {api-domain}/crm/v8/settings/recycle_bin/{record_ID}
DELETE {api-domain}/crm/v8/settings/recycle_bin?ids={id1,id2,...}
DELETE {api-domain}/crm/v8/settings/recycle_bin?filters={filter_value}
This endpoint permanently deletes one or more records from the Recycle Bin. Unlike a regular delete that moves records to the Recycle Bin, this removes them with no possibility of recovery.
A common use case is compliance-driven data erasure. For example, when a customer requests permanent deletion of their data under regulations like GDPR, the record can first be deleted from its module and then permanently removed from the Recycle Bin.

Endpoint: POST {api-domain}/crm/v8/settings/recycle_bin/actions/empty
This API permanently deletes all records currently present in the Recycle Bin.
This is useful at the end of a bulk data migration or a testing cycle, where large numbers of records have been deleted and need to be permanently purged without going through them individually.
Automating Deal recovery from the Recycle Bin
Now that we've covered the available Recycle Bin APIs, let's put them to work by building a recovery workflow for Zylker's integration.
Recall that Zylker’s integration creates Deals in CRM whenever a new order is placed. If an order fails, the corresponding Deal may be deleted. Later, if the order is corrected, the integration needs to restore the original Deal so that the sales team can continue working with the same record and retain its associated history.
To automate this process, we can implement a recovery workflow that performs the following steps:
Identify the deleted Deal that needs to be restored.
Retrieve the corresponding record from the Recycle Bin.
Restore the record programmatically.
Perform post-restore updates so the sales team knows the record was recovered.
The function below demonstrates how this can be implemented using Deluge.
The function supports two recovery paths:
Direct restoration using a record ID when the integration already knows the Deal ID in CRM.
Search-based restoration using the Deal name and a time window, which helps locate the correct record when the ID is not available.
Once the Deal is restored, the function also performs a few post-restore actions:
Updates the Deal stage to Needs Analysis
Adds a note indicating that the Deal was restored and why
This keeps the sales team informed and preserves a clear activity history.
The function has three parameters:
deal_name (string) - The name or partial name of the Deal to search for
days (integer) - The number of days to look back from the current day
record_id (string) - The CRM record ID of the deleted Deal, if known.
The record_id parameter is optional. If provided, the function skips the search and restores the record directly. If not, it falls back to searching by deal_name and days. This makes the function flexible enough to handle both scenarios: integrations that track CRM record IDs externally, and those that don't.
Before you begin:
This function uses a Zoho OAuth connection named crm_oauth_connection to make authenticated API calls to the Recycle Bin endpoints internally. Before running the function, set it up as follows:
Go to Setup > Developer Hub > Connections
Click Create Connection
Choose Zoho CRM as the service
Set the connection name to crm_oauth_connection (this must match exactly what is used in the code)
Add the following scopes:
ZohoCRM.modules.ALL
ZohoCRM.settings.recycle_bin.READ
ZohoCRM.settings.recycle_bin.UPDATE
Click Create and Connect and complete the authorization
Note: If this connection expires at any point, the function will return an UnAuthenticated Connection error. Go back to the Connections page and reauthorize it to resolve this.
The full implementation of the recovery function is available for download here : restoreDeletedDeal
Let's briefly walk through the logic implemented in the function.
1. The recovery path
The function first determines which recovery path to follow based on whether a record_id is provided.
if(record_id != null && record_id.trim() != "") |
If record_id is a non-empty string, the function restores the Deal directly using that ID. This is the most reliable approach because it avoids ambiguity and ensures that the correct record is restored.
If the ID is not provided, the function falls back to searching the Recycle Bin using the Deal name and a configurable time window.
2. Searching the Recycle Bin
When the record ID is unavailable, the function retrieves deleted records and filters them to locate the correct Deal.
The filtering logic ensures that:
Only records from the Deals module are considered
The Deal name matches the input
The record was deleted within the specified time window.
This helps narrow down the correct record and avoid restoring unrelated Deals.
if(moduleApiName == "Deals" && displayName.trim().toLowerCase().contains(trimmedName) && deletedDate >= lookbackDate) |
3. Handling search results
If no matching records are found, the function returns a not found message. If exactly one match is found, its ID and name are stored and the function proceeds to the restore step. If multiple matches are found, the function returns a structured list instead of guessing which one to restore:
if(matchedRecords.size() > 1) { resultMsg = "Multiple Deals found matching '" + deal_name + "'. Please call this function again with the correct record_id:\n"; for each match in matchedRecords { resultMsg = resultMsg + "- ID: " + match.get("id") + " | Name: " + match.get("deal_name") + " | Deleted On: " + match.get("deleted_time") + " | Deleted By: " + match.get("deleted_by") + "\n"; } return resultMsg; } |
The caller must review the list, identify the correct record, and call the function again with the record_id directly.
4. Restoring the Record
Once the correct record ID is confirmed, the function calls the Restore endpoint.
restoreResponse = invokeurl [ url: "https://www.zohoapis.com/crm/v8/settings/recycle_bin/" + matchedRecordId + "/actions/restore" type: POST connection: "crm_oauth_connection" ]; |
The response is checked for two possible success codes - SUCCESS for an immediate restore, and SCHEDULED when the record has more than 1000 associated records and the restore runs as a background job. Both are handled as successful outcomes with different return messages.
5. Post-restore updates
After a successful restore, the function updates the Deal stage and adds a Note:
dealUpdate.put("Stage", "Needs Analysis"); zoho.crm.updateRecord("Deals", matchedRecordId.toLong(), dealUpdate); noteContent = "Deal restored by " + currentUserName + " on " + currentDate + ". Reason: Order corrected."; zoho.crm.createRecord("Notes", note); |
Both updates are placed after the restore call. If the restore fails, neither runs, keeping the CRM data consistent.
Calling the function via REST API
One of the powerful capabilities of Zoho CRM is that standalone Deluge functions can be exposed as REST API endpoints and called from any external system. This is what allows Zylker's e-commerce backend to trigger the recovery workflow directly whenever an order is corrected.
Enabling the Function as a REST API
Once the function is saved, follow these steps to expose it as a REST API:
Go to Setup > Developer Hub > Functions
Click the ⋯ (three dots) next to the corresponding function (restoreDeletedDeal)
Click REST API
Enable the OAuth2 slider
Copy the endpoint URL shown
Click Save.
The endpoint will be in the following format:
POST https://www.zohoapis.com/crm/v7/functions/{function-name}/actions/execute?auth_type=oauth
While calling the function, arguments must be passed in the request body as a form-data parameter named arguments, with the values as a JSON string.
Sample: {"deal_name":"Zylker","days":10,"record_id":""}
Required scope: ZohoCRM.functions.execute.CREATE
Conclusion
The Recycle Bin API is a powerful but often overlooked capability in Zoho CRM. As we've seen in Zylker's scenario, restoring deleted records programmatically isn't just a convenience. It is a data integrity requirement for integrations that need to maintain a consistent, unbroken history of records across systems.
We hope this post helps you build more resilient and data-safe integrations on Zoho CRM. If you have questions or want to share how you've used the Recycle Bin API in your own integrations, drop a comment below!