Audited apps/zoho/actions/ against the workbook sheet VERIFIED Zoho, but treated the real action code plus canonical Zoho docs as ground truth. This workbook snapshot had 12 FAIL/mismatch rows and 0 need review rows for Zoho.
Every changed action below was verified against the actual HTTP call in code before updating _scopes.
| Action | Old | New | Reason | Source |
|---|---|---|---|---|
ZOHO_CONVERT_LEADS_TO_RECORD | {"all_of":[{"any_of":["ZohoCRM.modules.leads.ALL"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.leads.CREATE"]}]} | POST /crm/v8/Leads/{lead_id}/actions/convert documents ZohoCRM.modules.ALL or ZohoCRM.modules.leads.CREATE; leads.ALL is not listed on the canonical page, so the compatibility alias added in the first bugbot pass was reverted. | convert-lead v8 |
ZOHO_CREATE_RECORD | {"all_of":[{"any_of":["ZohoCRM.modules.ALL"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.WRITE","ZohoCRM.modules.{module_name}.CREATE"]}]} | POST /crm/v7/{module_api_name} allows global modules.ALL or module-level ALL / WRITE / CREATE; Mercury only accepted the global scope. | insert-records v7 |
ZOHO_CREATE_TAG | {"all_of":[{"any_of":["ZohoCRM.settings.tags.CREATE"]}]} | {"all_of":[{"any_of":["ZohoCRM.settings.ALL","ZohoCRM.settings.tags.ALL","ZohoCRM.settings.tags.WRITE","ZohoCRM.settings.tags.CREATE"]}]} | POST /crm/v8/settings/tags accepts settings.ALL or tag ALL / WRITE / CREATE; Mercury only accepted tags.CREATE. | create-tag v8 |
ZOHO_GET_MODULE_FIELDS | {"all_of":[{"any_of":["ZohoCRM.settings.fields.READ"]}]} | {"all_of":[{"any_of":["ZohoCRM.settings.fields.READ","ZohoCRM.settings.fields.ALL","ZohoCRM.settings.ALL"]}]} | GET /crm/v8/settings/fields accepts fields.READ, fields.ALL, or settings.ALL; Mercury only allowed the narrowest option. | field-meta v8 |
ZOHO_GET_RECORDS | {"all_of":[{"any_of":["ZohoCRM.modules.ALL"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.READ"]}]} | GET /crm/v8/{module_api_name} allows global ALL or module ALL / READ; Mercury only accepted the global scope. | get-records v8 |
ZOHO_GET_RELATED_RECORDS | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module_name}.READ"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.READ"]}]} | GET /crm/v8/{module_api_name}/{record_id}/{related_list_api_name} also accepts module-level ALL; Mercury omitted that valid alternative. | get-related-records v8 |
ZOHO_LIST_MODULES | {"all_of":[{"any_of":["ZohoCRM.settings.modules.READ","ZohoCRM.settings.ALL"]}]} | {"all_of":[{"any_of":["ZohoCRM.settings.ALL","ZohoCRM.settings.modules.ALL","ZohoCRM.settings.modules.READ"]}]} | GET /crm/v8/settings/modules also accepts ZohoCRM.settings.modules.ALL; the workbook spec row was narrower than the canonical doc. | module-meta v8 |
ZOHO_LIST_RECORD_ATTACHMENTS | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.READ","ZohoCRM.modules.attachments.ALL","ZohoCRM.modules.attachments.READ"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.READ"]},{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.attachments.ALL","ZohoCRM.modules.attachments.READ"]}]} | GET /crm/v8/{module_api_name}/{record_id}/Attachments requires modules.ALL or (module scope and attachments scope). The old single OR clause let attachments.READ-only tokens pass Mercury and then 403. | get-attachments v8 |
ZOHO_SEARCH_RECORDS | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoSearch.securesearch.READ"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.READ"]},{"any_of":["ZohoSearch.securesearch.READ"]}]} | GET /crm/v8/{module_api_name}/search requires module scope and ZohoSearch.securesearch.READ; the old OR clause allowed either one alone, and the doc does not list global modules.ALL here. The compatibility alias added in the first bugbot pass was reverted. | search-records v8 |
ZOHO_UPDATE_RECORDS | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module}.CREATE","ZohoCRM.modules.{module}.WRITE"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module}.ALL","ZohoCRM.modules.{module}.WRITE","ZohoCRM.modules.{module}.UPDATE"]}]} | PUT /crm/v7/{module_api_name} documents module ALL / WRITE / UPDATE; CREATE was the wrong operation and UPDATE / module-level ALL were missing. | update-records v7 |
ZOHO_UPDATE_RELATED_RECORD | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module}.READ"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module}.ALL","ZohoCRM.modules.{module}.WRITE","ZohoCRM.modules.{module}.UPDATE"]}]} | PUT /crm/v7/{module_api_name}/{record_id}/{related_list_api_name} is an update endpoint, so READ was invalid; doc lists module ALL / WRITE / UPDATE. | update-related-records v7 |
ZOHO_UPLOAD_ATTACHMENT | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.attachments.READ","ZohoCRM.modules.{module}.READ"]}]} | {"all_of":[{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module}.ALL","ZohoCRM.modules.{module}.WRITE","ZohoCRM.modules.{module}.CREATE"]},{"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.attachments.ALL","ZohoCRM.modules.attachments.WRITE","ZohoCRM.modules.attachments.CREATE"]}]} | POST /crm/v8/{module_api_name}/{record_id}/Attachments requires modules.ALL or (module scope and attachments scope) with write/create-class operations. The old READ scopes would authorize Mercury but not the upload endpoint. | upload-attachment v8 |
ZOHO_LIST_RECORD_ATTACHMENTSCode path: GET /crm/v8/{module_api_name}/{record_id}/Attachments in apps/zoho/actions/list_record_attachments.py.
| Endpoint | Doc requirement | CNF clause shipped |
|---|---|---|
GET /crm/v8/{module_api_name}/{record_id}/Attachments | ZohoCRM.modules.ALL or ZohoCRM.modules.{module_name}.ALL/READ | {"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.READ"]} |
GET /crm/v8/{module_api_name}/{record_id}/Attachments | ZohoCRM.modules.ALL or ZohoCRM.modules.attachments.ALL/READ | {"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.attachments.ALL","ZohoCRM.modules.attachments.READ"]} |
ZOHO_SEARCH_RECORDSCode path: GET /crm/v8/{module_api_name}/search in apps/zoho/actions/search_records.py.
| Endpoint | Doc requirement | CNF clause shipped |
|---|---|---|
GET /crm/v8/{module_api_name}/search | module scope ZohoCRM.modules.{module_name}.ALL/READ | {"any_of":["ZohoCRM.modules.{module_name}.ALL","ZohoCRM.modules.{module_name}.READ"]} |
GET /crm/v8/{module_api_name}/search | ZohoSearch.securesearch.READ | {"any_of":["ZohoSearch.securesearch.READ"]} |
ZOHO_UPLOAD_ATTACHMENTCode path: POST /crm/v8/{module_api_name}/{record_id}/Attachments in apps/zoho/actions/upload_attachment.py.
| Endpoint | Doc requirement | CNF clause shipped |
|---|---|---|
POST /crm/v8/{module_api_name}/{record_id}/Attachments | ZohoCRM.modules.ALL or ZohoCRM.modules.{module}.ALL/WRITE/CREATE | {"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.{module}.ALL","ZohoCRM.modules.{module}.WRITE","ZohoCRM.modules.{module}.CREATE"]} |
POST /crm/v8/{module_api_name}/{record_id}/Attachments | ZohoCRM.modules.ALL or ZohoCRM.modules.attachments.ALL/WRITE/CREATE | {"any_of":["ZohoCRM.modules.ALL","ZohoCRM.modules.attachments.ALL","ZohoCRM.modules.attachments.WRITE","ZohoCRM.modules.attachments.CREATE"]} |
None in this workbook snapshot:
VERIFIED Zoho had no need review rows.FAIL row mapped to a real _scopes correction.uv run nox -s fmt_app -- apps/zoho/actions./.nox/fmt_app/bin/ruff format <edited files>uv run nox -s chk_app -- apps/zoho/actions./.nox/chk_app/bin/ruff check <edited files>ast.literal_eval against every edited _scopes blockZOHO_SEARCH_RECORDS and ZOHO_CONVERT_LEADS_TO_RECORD.58ee8662ff temporarily restored ZohoCRM.modules.ALL and ZohoCRM.modules.leads.ALL as literal compatibility aliases because Mercury's scope matcher does not infer hierarchy.f0ae802c78 reverted both aliases after re-checking the audit hard rule: never ship a scope that does not appear on the canonical endpoint doc page. Final shipped scopes now match the Zoho docs again.Reverted from the first bugbot pass:
ZOHO_SEARCH_RECORDS: removed undocumented ZohoCRM.modules.ALL from the first clause.ZOHO_CONVERT_LEADS_TO_RECORD: removed undocumented ZohoCRM.modules.leads.ALL from the clause.Why the revert: the compatibility aliases may help existing connections pass Mercury's literal scope gate, but they violate the audit policy and canonical Zoho documentation for these specific endpoints. The final PR favors doc-grounded correctness over undocumented compatibility scopes.