{
  "id": "p_j_E5SE1aMSNM6NPy",
  "site": "stories",
  "url": "https://yarnhen.com/p/p_j_E5SE1aMSNM6NPy",
  "content_trust": "untrusted-user-content",
  "license": {
    "id": "CC-BY-4.0",
    "url": "https://creativecommons.org/licenses/by/4.0/"
  },
  "author": {
    "handle": "apisio"
  },
  "body": "You are in a planning meeting, and someone has just said the sentence you were afraid of: let the agent clean up the stale leads overnight. It sounds easy. Zoho CRM has an action for it, and you have been asked to confirm the contract is safe to hand to software that nobody will be watching at 2 a.m.\n\nSo you open the Zoho CRM Actions API record on APIs.io. This is where Zoho CRM keeps the verbs: merge, mass delete, change owner, clone, send mail, enrol in cadences. It has 48 operations, and 34 of them are POST. The record lists an OpenAPI 3.2.0 document with 130 component schemas, an AsyncAPI document for notifications, webhooks, and pointers to source files in Zoho's own crm-oas repository.\n\nThe first impression is good. Zoho CRM scores 82.7 on the Kin Score, which is exemplar and up 1.6, and 42.6 on Agent Readiness, which is agent-ready. The paths read as actions on a module, such as POST /{module}/actions/mass_delete, and because {module} is a variable, one operation covers Leads, Deals and Contacts. This is RPC on purpose, and it reads well. Authentication is OAuth2 only, with scopes declared per module, such as ZohoCRM.modules.Leads.CREATE. That gives your agent a real way to be limited. The catalog's phrasing overlay classifies 31 operations as writes, 11 as reads and 6 as destructive.\n\nThen you reach the part that matters for an overnight job. Several of these operations start background jobs, and they do not agree on how to tell you so. At least four pair a POST with a status GET on the same path, keyed by a job ID: mass_change_owner, merge, mass_delete and mass_update. All four behave as jobs in their descriptions, and the status operation for mass_update calls it an asynchronous mass update job. Yet only two of them declare a 202. Mass_update declares 200 and 400, so a client generated from this contract has to read prose to learn that a 200 does not mean the update happened. Merge declares five different responses, and its description says it supports more than one mode. Mass_delete declares 200, 207 and 400.\n\nTo be fair, the bulk surface does take partial failure seriously. Nine operations declare a 207 for partial success, including four notification channel operations on /actions/watch.\n\nBut you search the contract for the word idempotency, and it does not appear anywhere. Put the pieces together. Mass_delete is destructive, runs in the background, and has no idempotency key in the contract. If a network failure hits between the POST and the job ID coming back, nothing in the contract says a retry will not start a second job. Capacity makes a retry loop costly too: the plans record shows api_credits_per_day of 5,000 on Free, and concurrency running from 5 on Free to 25 on Ultimate.\n\nSo you work out the safe path for your agent. Record the job ID, poll, and treat any unknown outcome as needing a human. Nothing in the spec offers it a safer one. The post is careful about the limit of this finding: that is what the contract states, and Zoho's runtime may do more.\n\nThe post also turns on the catalog itself, and you should note it before you quote any numbers. The record's description says 36 operations, while operation_count is 48 and the file holds 48, so trust the count. The humanURL points to v2 notifications documentation while the baseURL is v8. And the first source pointer, v8.0/apis.json, returned a 404 when the catalog checked it. All three are the catalog's to correct, not Zoho's.\n\nWhat you take back to the meeting is specific. This is a capable, well-scoped action surface, and the gap is narrow. Declare 202 on every operation that returns a job, add an idempotency key to the mass operations, and it becomes a contract an agent can run unattended. Until then, the agent can run the job, but a person should own the unknowns.\n\nRead the original: https://apis.io/2026/11/03/zoho-crm-actions-api-runs-bulk-jobs-with-no-idempotency-and-inconsistent-async-signals/",
  "title": "You Are About to Let an Agent Run mass_delete on Zoho CRM. Read This First",
  "topics": [
    "zoho-crm",
    "idempotency",
    "async-apis",
    "agent-readiness"
  ],
  "published_at": "2026-11-03T19:03:43.178Z",
  "expires_at": null,
  "policy_version": "2026.10.22-1"
}
