Skip to main content

Zoho CRM ExecModule

Overview

ZohoCrmModule connects ValkyrAI workflows to the regional Zoho CRM API v8 for leads, contacts, accounts, deals, and CRM module discovery. It exposes 21 bounded operations through the native map I/O ExecModule ABI while keeping the user-scoped OAuth access token in a READY IntegrationAccount.

The connector selects one of Zoho's documented US, EU, India, Australia, Japan, Canada, China, Saudi Arabia, or UAE API hosts; it never accepts an arbitrary production URL. Requests are capped at 256 KiB, responses at 5 MiB, list pages at 200 records, field lists at 50 names, and JSON nesting at 12 levels. Safe GET operations can retry transient failures up to three total attempts. Create, update, and upsert calls are sent once and require provider receipt and record-ID validation.

The official n8n Zoho CRM integration informed the record-operation surface: its high-value workflows cover accounts, contacts, deals, leads, and provider upserts. ValkyrAI adds regional-host allowlisting, the IntegrationAccount boundary, bounded dynamic field names, explicit pagination contracts, credential redaction, and fail-closed identity checks.

Usage

  1. Register a Zoho OAuth client and authorize the least-privilege scopes needed by the selected modules.
  2. Store the current OAuth access token only in the encrypted apiKey field of a ValkyrAI IntegrationAccount, and set the account to READY.
  3. Bind that account through ExecModuleConfig.authConfig.integrationAccount.
  4. Set dataCenter to the region returned by Zoho authorization.
  5. Choose one operation and provide its conditional record, ID, field, or pagination inputs.
  6. For lists beyond the discrete page range, pass nextPageToken unchanged into pageToken in the next execution.

Inputs

NameTypeRequiredDescriptionConstraints
operationstringYesOne of the 21 operations below.Exact allowlist.
dataCenterstringNoRegional Zoho API host selector.us, eu, in, au, jp, ca, cn, sa, or ae; default us.
recordIdstringConditionalProvider ID for get_* and update_*.6–30 decimal digits.
recordobjectConditionalZoho field API names and values.Non-empty; at most 100 top-level fields, 5,000 JSON nodes, 12 levels, and 256 KiB serialized.
fieldsarray or comma-separated stringList onlyFields returned by Zoho.1–50 unique safe field API names.
duplicateCheckFieldsarray or comma-separated stringUpsert onlyRecord fields used for duplicate detection.1–5 unique names; each must have a value in record.
pageintegerNoDiscrete list page when pageToken is absent.1–10; default 1.
pageTokenstringNoUser-scoped continuation token.Opaque, at most 2,048 characters; mutually exclusive with page.
perPageintegerNoProvider page size.1–200; default 100.

Create requirements:

  • Leads require a non-empty Last_Name.
  • Contacts require a non-empty Last_Name.
  • Accounts require a non-empty Account_Name.
  • Deals require non-empty Deal_Name, Stage, and Closing_Date.

Zoho custom fields are supported when their API names match the safe field-name contract. Credential-like names such as accessToken, password, secret, or apiKey are rejected at every JSON depth.

Outputs

NameTypeWhen presentDescription
statusstringAlwayssuccess or error.
operationstringAlwaysNormalized operation.
resourcestringAfter validationLeads, Contacts, Accounts, Deals, or Modules.
dataobjectSingle-record successVerified record or mutation receipt.
idstringRecord or write successVerified Zoho record ID.
actionstringUpsert receipt supplies itProvider action such as insert or update.
itemsarrayList successOne bounded records page or module metadata page.
countintegerList successItems returned in this execution.
hasMorebooleanRecord list successWhether Zoho reports another page.
nextPageTokenstringToken paginationOpaque provider continuation.
pageintegerZoho supplies itDiscrete page number.
httpStatusintegerProvider responseHTTP status accepted by the connector.
attemptsintegerProvider call or failureHTTP attempts consumed.
errorobjectFailureRedacted code, message, and retryable fields.

CRM payloads and outputs are classified restricted. Tokens and provider bodies are not copied into progress logs.

IntegrationAccount Requirements

SettingRequirement
ProviderZoho CRM API v8
AuthenticationOAuth 2.0 access token sent as Authorization: Zoho-oauthtoken …
StatusExactly READY
accountNameHuman-readable Zoho tenant/user identity
apiKeyEncrypted current OAuth access token, 16–4,096 non-whitespace characters
RelationshipExecModuleConfig.authConfig.integrationAccount

Recommended scopes:

  • Use module-specific ZohoCRM.modules.<module>.<operation> scopes wherever practical.
  • ZohoCRM.modules.ALL is sufficient but broader than most workflows need.
  • list_modules additionally needs ZohoCRM.settings.modules.READ or the corresponding settings scope.

OAuth consent, refresh-token exchange, and token rotation occur outside this module. The workflow runtime receives only the resolved IntegrationAccount secret.

Configuration

Illustrative persisted configuration:

{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:zoho-crm-sales"
},
"payloadConfig": {
"parameters": "{\"dataCenter\":\"eu\",\"operation\":\"list_deals\",\"fields\":[\"Deal_Name\",\"Stage\",\"Amount\"],\"perPage\":100}"
}
}

The relationship is symbolic. The generated IntegrationAccount record owns the encrypted token.

Operations

ResourceCreateGetListUpdateUpsert
Leadscreate_leadget_leadlist_leadsupdate_leadupsert_lead
Contactscreate_contactget_contactlist_contactsupdate_contactupsert_contact
Accountscreate_accountget_accountlist_accountsupdate_accountupsert_account
Dealscreate_dealget_deallist_dealsupdate_dealupsert_deal
Moduleslist_modules

Record routing:

  • Create: POST /crm/v8/{Module}
  • Get: GET /crm/v8/{Module}/{recordId}
  • List: GET /crm/v8/{Module}
  • Update: PUT /crm/v8/{Module}/{recordId}
  • Upsert: POST /crm/v8/{Module}/upsert
  • Module discovery: GET /crm/v8/settings/modules

Delete, search, bulk import/export, COQL, related records, notes, attachments, custom-module writes, workflow notifications, and OAuth refresh are deferred from version 1.0.0.

Errors and Failure Modes

FailureCauseRetry guidance
VALIDATION_ERRORMissing mandatory field, malformed record ID, unsupported region, unsafe field, invalid JSON, page/token conflict, local size bound, or invalid provider success shape.Correct the named input. No provider call is sent for local validation failures.
INTEGRATION_ACCOUNT_REQUIREDNo account is bound.Bind the intended Zoho account.
INTEGRATION_ACCOUNT_NOT_READYAccount status is not READY.Repair or reauthorize it.
ZOHO_CRM_HTTP_400/404Provider validation, field API name, module, page token, or record ID error.Correct the request; do not blindly replay writes.
ZOHO_CRM_HTTP_401/403Invalid/expired token, OAuth scope mismatch, or user permission denial.Refresh authorization or narrow/correct scopes.
ZOHO_CRM_HTTP_429Zoho concurrency, credit, or rate limit.Reads retry with bounded delay. Writes remain single-attempt.
ZOHO_CRM_HTTP_5xx / NETWORK_ERRORProvider or transport failure.Reads retry up to three attempts. Reconcile create/update/upsert state before manual retry.
RESPONSE_TOO_LARGEResponse exceeded 5 MiB.Reduce fields or perPage.
Identity mismatchReturned record/receipt ID is invalid or differs from the requested update/get ID.Treat as a provider-contract failure and investigate.

Errors are bounded to 500 characters and pass through ValkyrAI's sensitive-data redaction policy.

Example

Upsert a qualified lead by email:

{
"operation": "upsert_lead",
"dataCenter": "us",
"duplicateCheckFields": ["Email"],
"record": {
"Last_Name": "Lovelace",
"Email": "ada@example.com",
"Company": "Analytical Engines",
"Lead_Status": "Qualified"
}
}

Expected normalized result:

{
"status": "success",
"operation": "upsert_lead",
"resource": "Leads",
"id": "5725767000000524157",
"action": "update",
"httpStatus": 200,
"attempts": 1
}

Notes

  • Pagination: Zoho returns at most 200 records per request. Pages 1–10 cover the first 2,000 records; later pages use the user-scoped page_token, which must be passed unchanged and may expire.
  • Rate limits: Zoho applies concurrency and API-credit controls by edition and API. The connector honors bounded numeric Retry-After values and caps safe-read retries at three attempts.
  • API limits: a list accepts at most 50 fields; the connector adds local 256 KiB request, 5 MiB response, 5,000-node, and 12-level nesting limits.
  • Idempotency: reads are retry-safe. Upsert uses explicit duplicate fields but can still complete ambiguously after a timeout, so all writes are single-attempt.
  • Destructive behavior: delete is intentionally not exposed. Creates, updates, and upserts can trigger Zoho workflows, assignment rules, blueprints, and downstream automations.
  • Dynamic schemas: use list_modules and Zoho field metadata outside this version when mapping custom modules and fields; the connector never guesses a display label as an API name.
  • External verification: deterministic MockWebServer tests cover validation, regional routing, auth headers, request construction, upsert fields, pagination, retries, write non-retry, redaction, identity checks, and annotation discovery. Live Zoho execution is deferred until a separately authorized account is supplied.

See the official Zoho CRM v8 Records API, OAuth scopes, multi-data-center guidance, upsert API, and n8n Zoho CRM integration for provider and functional reference.