Skip to content

Integrations

Zoho CRM SDK: get started

The complete Zoho CRM API through typed application or agent clients

Use an ordinary App, or an Agent when the workload needs agent identity. Alter builds provider requests from a reviewed contract and applies the same connection authorization, policy, approvals, and audit records as raw requests.

Terminal window
npm install @alter-ai/sdk @alter-ai/zoho
# Python (installs core automatically)
pip install alter-sdk-zoho

The provider package includes only Zoho CRM types and response schemas and pins its contract version.

Configure Zoho CRM, request the scopes the application needs (the default scopes read records and the organization, so writes need scopes chosen at connect), and retain each customer’s grant ID after Connect consent. Alter supplies the credential; it never belongs in operation input.

import { App } from "@alter-ai/sdk";
import { ZohoIntegration } from "@alter-ai/zoho";
const app = new App({ apiKey: process.env.ALTER_API_KEY! });
try {
const client = new ZohoIntegration(app, { grantId: process.env.ZOHO_GRANT_ID! });
const result = await (await client.org.getOrganization({})).wait();
console.log(result.bodyJson());
} finally {
await app.close();
}
from alter_sdk import App
from alter_sdk_zoho import ZohoIntegration
async def first_call(api_key: str, grant_id: str):
async with App(api_key=api_key) as app:
client = ZohoIntegration(app, {"grant_id": grant_id})
call = await client.org.get_organization({})
result = await call.wait()
return result.body_json()

Contract 2026-10-31 contains every operation in Zoho’s official CRM v8 OpenAPI documents, one per resource, pinned at one commit. The API reference links every operation to its complete input fields, response models, TypeScript and Python examples, and policy identity. Browse by resource under this provider’s API reference in the Integrations sidebar, or download the pinned OpenAPI specification. The Developer Portal provider view and alter integrations operations zoho --version 2026-10-31 also expose the schemas.

Generated calls pin the Alter contract version. Zoho CRM’s own product, role, scope and subscription requirements still apply. There is no silent version fallback and no automatic write retry or pagination.

Terminal window
alter integrations execute zoho zoho.getorganization \
--version 2026-10-31 --grant "$ZOHO_GRANT_ID" --input '{}' --wait

Arguments use native path, query, headers, and body containers. Array query parameters are sent the way the provider’s document declares them: one delimited value where it declares explode: false (or a pipe- or space-delimited style), repeated keys otherwise. SDK methods preserve provider field names, decimal lexemes and large integers as strings when necessary. HTTP 204 returns null / None from the typed decoder. Binary responses use bodyBytes() / body_bytes(); the CLI can save them with --output-file.

A policy rule can require approval for any operation. The call returns a pending approval; wait() resolves once the approver decides, and resume(approvalId) picks a pending approval up after a restart. Approval cards show the operation, its contract version and the request input the sensitivity review marked safe to display.

Four operations upload a file: attachments.upload_attachment (a file, or attachmentUrl to link one), files.upload_files (up to 10 files), record_photo.upload_record_photo and org_photo.upload_organization_photo. Every binary input is a JSON object with filename, content_type, and content_base64; Alter sends each body field as a named multipart part.

Uploaded file contents are classified sensitive. An upload a policy sends for approval is held with the pending approval, and the approver sees each file masked as its name and size, never its contents. The shared proxy accepts up to 5 MiB of serialized input and encoded upload data, and base64 adds overhead.

Zoho’s bulk upload (upload.zoho.<data center>) is not published: it sits outside the API hosts a connection allows and needs a WorkDrive scope the connection never requests, so bulk_write.create_bulk_write_job needs a file_id obtained outside Alter.

Every operation’s policy decision (its families, whether it hands over a credential, its sensitive request fields and the parameters a rule can read) was reviewed against Zoho’s page for that operation, and a new operation without a review fails generation. The per-operation families are in the Zoho CRM connection’s policy table. Operation names in stored rules are Zoho’s own operationIds, the names the raw proxy’s catalog has always used.

  • read is only for an operation that changes nothing, including a POST that only reads (a COQL query, a bulk read job). Every other operation carries write, and a removal also carries delete.
  • payment marks currencies, inventory conversions and other changes to what is charged.
  • send marks anything that makes Zoho email or notify a person, including record writes that can carry send_notification and automation actions with a notify flag.
  • admin marks settings, users, roles, profiles, sharing, notification channels and webhooks.

Sensitive request fields are held with a pending approval and shown masked: dates of birth, notification verification tokens, webhook parameter values, variable values, composite sub-request bodies and headers, and uploaded files.

Payload capture follows the application’s capture setting, on managed and raw proxied calls alike: credentials are removed, every field Alter’s reviewed inventory for the provider marks sensitive is masked in requests and responses, including fields it locates only by operation, and binary bodies, calls that can return a credential and calls carrying raw card data are never stored. Metadata audit is unchanged. A portal update that sets a SAML public_key is refused by default with credential_minting_refused until an operator adds a credential_minting allowance naming zoho.updateportal.

Four operations can leave the caller holding a credential Zoho stored, so Alter refuses them by default on every connection with credential_minting_refused, before any call reaches Zoho:

OperationWhat the caller would hold
zoho.getvariables, zoho.getvariablebyidVariable values, which Zoho documents as reusable authentication parameters
zoho.getwebhookbyidA webhook’s header and parameter values, where API keys for the external application live
zoho.createcompositerequestAny of the above: a sub-request can read them, and the refusal matches the outer route only

Reading notification channels (zoho.getnotifications) is not refused: a channel’s verification token only lets the receiver check that a callback came from Zoho and grants no access to Zoho.

The methods stay in the SDK. An organization admin who needs one allows it with a credential_minting rule naming provider zoho and the operation, outright or behind an approval; see Allow an operation that hands over a credential.

A Zoho connection lives in one data center, and each call goes to that connection’s own API domain: www, sandbox or developer on zohoapis.com, .eu, .in, .com.au, .jp, .ca, .sa, .uk, .ae or .sg. A connection with no recorded API domain fails with connection_origin_missing until it is reconnected, and one outside these hosts fails with connection_origin_not_allowed.

A batch Zoho partly applied answers 207, and each item in the response carries its own status and code. The call succeeds and the caller reads each item: some writes happened, so retrying the whole batch would repeat them. A batch that wholly failed is an ordinary Zoho error.

The pinned official source, its hash, the generated methods and the policy mapping are checked together. Scheduled checks save upstream candidates and report additions, removals and schema changes; they never replace a published contract automatically. Existing contract versions remain available for installed SDKs and pending approvals.

Persist call.approvalId (TypeScript) or call.approval_id (Python), then construct a fresh provider client and call resume(savedId), followed by wait(). The stored approval selects the operation; this installed package verifies its provider and contract version before attaching the response validator. Mismatches raise ManagedOperationError, never silently decode using another contract. See persist-and-resume examples.

Policy operation fields also accept the published SDK function IDs listed by alter integrations operations zoho. Use when.operation for a json_match or conditional require_approval rule, or match.operations for content_match (including redaction and step-up). Conditional approval can also use when.match.operations with parameter conditions. alter policy simulate --operation <function-id> tests that exact function.

Managed calls carry a server-verified function identity. Alter still evaluates policy against the actual method, URL and body after building the request from the authorized connection. Existing endpoint, method, host, catalog-operation and family rules continue to apply unchanged. A raw call can share an endpoint with several SDK functions and matches all of them; a function name never grants permission or bypasses existing restrictions.

For example, this rule requires approval for one function:

{
"effect": "require_approval",
"when": { "operation": "zoho.sendmail" },
"approval": { "approvers": ["reviewer@example.com"] }
}

Save it with alter policy rules create --app <app-id> --grant <grant-id> --type require_approval --body @rule.json, or select the function in the portal.

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.