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.
Install
Section titled “Install”npm install @alter-ai/sdk @alter-ai/zoho# Python (installs core automatically)pip install alter-sdk-zohoThe provider package includes only Zoho CRM types and response schemas and pins its contract version.
Connect and call
Section titled “Connect and call”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 Appfrom 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()Complete versioned contract
Section titled “Complete versioned contract”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.
alter integrations execute zoho zoho.getorganization \ --version 2026-10-31 --grant "$ZOHO_GRANT_ID" --input '{}' --waitArguments 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.
Approvals
Section titled “Approvals”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.
Uploads and approvals
Section titled “Uploads and approvals”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.
Policy and sensitive data
Section titled “Policy and sensitive data”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.
readis only for an operation that changes nothing, including a POST that only reads (a COQL query, a bulk read job). Every other operation carrieswrite, and a removal also carriesdelete.paymentmarks currencies, inventory conversions and other changes to what is charged.sendmarks anything that makes Zoho email or notify a person, including record writes that can carrysend_notificationand automation actions with anotifyflag.adminmarks 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:
| Operation | What the caller would hold |
|---|---|
zoho.getvariables, zoho.getvariablebyid | Variable values, which Zoho documents as reusable authentication parameters |
zoho.getwebhookbyid | A webhook’s header and parameter values, where API keys for the external application live |
zoho.createcompositerequest | Any 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.
Data centers
Section titled “Data centers”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.
Batch results
Section titled “Batch results”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.
Maintenance
Section titled “Maintenance”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.
Resume after a restart
Section titled “Resume after a restart”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.
Rules for SDK functions
Section titled “Rules for SDK functions”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.