> For the complete documentation index, see [llms.txt](https://docs.appmixer.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.appmixer.com/connector-configuration/zoho.md).

# Zoho

Configure the Zoho OAuth2 application for the Zoho CRM and Zoho Books connectors and prepare a Zoho CRM organization.

The Zoho connector has two modules, **Zoho CRM** and **Zoho Books**. Both connect through the same kind of Zoho OAuth client, and one client can serve both.

Connecting Zoho CRM takes two parts. The first is an OAuth client that Appmixer uses to connect accounts. The second is a Zoho CRM organization whose edition and settings expose the modules the components work with. Most "the component returns an error" problems with Zoho CRM come from the second part, not from Appmixer.

## Signup for Developer Account

Create a developer account by visiting <https://www.zoho.com/developer/signup.html>

## Setup Client from API Console

Setup a ***Server-based Applications*** API client from Zoho API Console <https://api-console.zoho.com>

![](https://802996127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIAGKHlIqVKJe9agnFr14%2Fuploads%2Fgit-blob-8031517b84d6a690f3c718a2cb97e9b34131ef9c%2Fzoho-1.png?alt=media)

![](https://802996127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIAGKHlIqVKJe9agnFr14%2Fuploads%2Fgit-blob-928c4904d65809296eb20bc25d9c299376f368ee%2Fzoho-2.png?alt=media)

Fill in the client information

![](https://802996127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIAGKHlIqVKJe9agnFr14%2Fuploads%2Fgit-blob-79e5f1d3c31e3f4f46e4c2e66232c83c50edc88c%2Fzoho-3.png?alt=media)

* **Authorized Redirect URI**: `<your Appmixer API URL>/auth/zoho/callback`, for example `https://api.acme.appmixer.cloud/auth/zoho/callback`. Zoho CRM and Zoho Books use the same redirect URI.

Once you created the client, the `Client ID` and `Client Secret` will be generated. Add them in the [Appmixer Backoffice](https://github.com/Appmixer-ai/appmixer-docs-gitbook/tree/app-registrations/getting-started/services.md) as `clientId` and `clientSecret`:

* under the `appmixer:zoho` service for Zoho CRM;
* under the `appmixer:zoho:books` service for Zoho Books.

Each module reads the credentials of its own service, so Zoho Books needs its own entry even when it uses the same client as Zoho CRM.

![](https://802996127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIAGKHlIqVKJe9agnFr14%2Fuploads%2Fgit-blob-a4fa1eaa9e826ff2a862fffdf2591864fc4f64b0%2Fzoho-4.png?alt=media)

### Data centers (Multi-DC)

Zoho runs separate data centers, and a user signs in at the one their organization lives in. The connector supports these data centers:

| Data center          | Accounts server         | API                   |
| -------------------- | ----------------------- | --------------------- |
| United States        | `accounts.zoho.com`     | `www.zohoapis.com`    |
| Europe               | `accounts.zoho.eu`      | `www.zohoapis.eu`     |
| India                | `accounts.zoho.in`      | `www.zohoapis.in`     |
| Australia            | `accounts.zoho.com.au`  | `www.zohoapis.com.au` |
| China                | `accounts.zoho.com.cn`  | `www.zohoapis.com.cn` |
| Japan                | `accounts.zoho.jp`      | `www.zohoapis.jp`     |
| Canada               | `accounts.zohocloud.ca` | `www.zohoapis.ca`     |
| Saudi Arabia         | `accounts.zoho.sa`      | `www.zohoapis.sa`     |
| United Kingdom       | `accounts.zoho.uk`      | `www.zohoapis.uk`     |
| United Arab Emirates | `accounts.zoho.ae`      | `www.zohoapis.ae`     |
| Singapore            | `accounts.zoho.sg`      | `www.zohoapis.sg`     |

The connector finds each account's data center when the account is connected. It saves the data center with the account and sends all later token refreshes and API calls there. Accounts connected with an older connector version keep working without reconnecting. Accounts from a data center that is not in the list cannot connect. Zoho CRM 2.4.1 and Zoho Books 1.2.1 added Japan, Canada, Saudi Arabia, the United Kingdom, the United Arab Emirates and Singapore.

The OAuth client is registered in one data center. If your users are in more than one data center, set up the client like this:

1. Open the client in the API Console and go to its **Settings** tab.
2. Enable **Multi-DC**.
3. Enable every data center your users are in. Otherwise users from the other data centers cannot authorize the client.

{% hint style="warning" %}
**Use the same client secret for all data centers.** Zoho can issue a separate client secret for each data center of a Multi-DC client. Appmixer stores one `clientSecret` per service and sends it to every data center. If a data center has its own secret, accounts from that data center cannot connect. Keep one set of credentials for all data centers in the client's Multi-DC settings.
{% endhint %}

### Scopes

The connector requests these scopes; nothing needs to be configured on the client for them.

Zoho CRM, on every account:

* `ZohoCRM.modules.ALL`: read and write records (cases, quotes, invoices, appointments, notes, …).
* `ZohoCRM.users.ALL`: the connected user's profile (e-mail, region, time zone).
* `ZohoCRM.settings.fields.READ`: field and picklist metadata for the inspector.
* `ZohoCRM.notifications.ALL`: the webhook-based triggers (e.g. **CaseUpdated**, **CaseClosed**).

Zoho CRM, only when a custom module component is used (**FindCustomModules**, **GetCustomModule**, **CreateCustomModule**):

* `ZohoCRM.settings.modules.READ`: read module definitions.
* `ZohoCRM.settings.modules.ALL` and `ZohoCRM.settings.profiles.READ`: create a custom module and grant it to profiles (**CreateCustomModule** only).

Zoho Books asks for `ZohoBooks.settings.READ` on every account and adds the scopes of the components used in the flow, e.g. `ZohoBooks.invoices.CREATE`, `ZohoBooks.contacts.READ` or `ZohoBooks.accountants.READ`. **MakeApiCall** asks for `ZohoBooks.fullaccess.all`.

### Reconnecting an account and refresh tokens

Zoho issues a refresh token only when the user gives consent. Unless consent is requested explicitly, a user who has already authorized the client gets **no refresh token**. The account then works for one hour and fails after that:

* `appmixer account test` reports it as not valid.
* Flows fail with `This Zoho account has no refresh token, so it stops working about an hour after it was connected.`

The connector requests consent on every connect (`prompt=consent`), so the Zoho consent screen is shown each time — that is expected.

When Zoho refuses to issue or refresh a token, the error names Zoho's reason for each data center tried, e.g. `No Zoho data center agreed to issue an access token (us: invalid_client, eu: invalid_code, …)`. This usually means one of two things: the client is not enabled for the account's data center, or that data center uses a client secret different from the one in the Backoffice. See [Data centers (Multi-DC)](#data-centers-multi-dc). Zoho CRM before 2.4.0 and Zoho Books before 1.2.0 did not report these errors and failed later with `Access token not found for component …` instead.

With an older connector version, fix an account that stopped working after an hour like this:

1. Remove the Appmixer client from the user's **Connected Apps** in Zoho Accounts (`accounts.zoho.<data center>`).
2. Connect the account again.

### Fields cache

The Zoho CRM components build their inspector from the fields of a module (contacts, leads, …), which each organization defines in its CRM settings. To reduce the number of Zoho API requests while flows are designed (and avoid using up the API credits), Appmixer caches the field list. The cache is kept per connected Zoho user, because picklist values differ between organizations.

The cache lifetime is set by the optional `listFieldsCacheTTL` key of the `appmixer:zoho` service, in milliseconds. The default is 10 minutes (`600000`). The screenshot shows it lowered to one minute (`60000`):

![](https://802996127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIAGKHlIqVKJe9agnFr14%2Fuploads%2Fgit-blob-ac59bdd47ba35ddc728e8f04b8aa3c491d00fc0f%2Fzoho-5.png?alt=media)

## Prepare the Zoho CRM organization

### Edition

Cases, Quotes, Invoices and Appointments are not available in the **Free** edition of Zoho CRM. On such an organization, the API refuses them even for an administrator:

* API v3 and newer answer `NO_PERMISSION` ("permission denied to access the module").
* API v2 answers `INVALID_MODULE` ("the module name given seems to be invalid").

The components surface these errors as they are. A trial organization falls back to the Free edition when the trial ends.

A **Zoho One** trial does not unlock these modules in an existing Zoho CRM organization. Start the **Zoho CRM** trial, or subscribe to a paid CRM edition, for the organization itself. You can check the edition in Zoho CRM under **Setup → General → Company Settings**.

### Appointments need the Services module

The Appointments module (`Appointments__s`) exists only once the **Services** module is enabled. Only a CRM administrator can enable it, and the organization needs the Professional edition or higher. There is no separate "Appointments" entry in Setup:

1. Open **Setup → Customization → Modules and Fields**.
2. Enable the **Services** module.
3. The Appointments module is added automatically.
4. Grant the profiles that should use it access under the module's **Module Permissions**.

Until then, the appointment components (**CreateAppointment**, **FindAppointments**, **AcceptDeclineAppointment**, **AppointmentStarted**) fail with `INVALID_MODULE`.

To create appointments, the organization also needs at least one **service**. Create it in the Services module and make sure it has:

* at least one **member**, a CRM user who provides the service;
* **availability** (days and timings);
* a **location**: Business Address, Client Address, or both.

An appointment must be for a **contact** (or a record of a custom customer module), must start in the future, and must fall within the service's available timings. In **CreateAppointment**, the **Owner** must be a member of the service and the **Location** must be one the service allows. **Address** is required for Client Address.

### Accepting and declining appointments

Zoho CRM has no accept/decline flag on appointments, so the **AcceptDeclineAppointment** component maps the decision onto the appointment's `Status`:

* **Accept** keeps the appointment `Scheduled`.
* **Decline** sets it to `Cancelled`. You can add a cancellation reason: `By Customer` (Zoho's default) or `By Team`. You can also add a free-text note.

Zoho does not allow any change to a **cancelled** or **completed** appointment; it answers `NOT_ALLOWED`. A declined appointment therefore cannot be accepted afterwards.

### Picklists are organization specific

Zoho does not publish fixed values for `Cases.Status`, `Quotes.Quote_Stage` or `Invoices.Status`. Every organization can customize them. The components offer the organization's own values in the inspector and fall back to these defaults:

* **AcceptQuote**: `Confirmed`.
* **DeclineQuote**: `Closed Lost`.
* **CaseClosed**: `Closed`.

Change them if your organization uses other names, e.g. *Resolved*.

### Triggers

* **CaseUpdated** and **CaseClosed** use Zoho CRM notifications (webhooks). Zoho reports only *that* a case was edited, so **CaseClosed** remembers each case's last seen status. It fires only when a case moves into the closed status; later edits of an already closed case do not fire again.
* **InvoiceDueDate** and **InvoiceOverdue** poll. They evaluate "today" in the connected user's time zone, taken from the Zoho user profile, because Zoho date fields carry no time zone.
* **AppointmentStarted** polls for appointments whose start time has passed and skips cancelled ones.

## Zoho Books

Zoho Books uses the OAuth client described above, under the `appmixer:zoho:books` service. The connected Zoho user needs at least one Zoho Books organization; otherwise connecting fails with `No organizations found for this account.` The account takes its name from the e-mail of the user's default organization. The components ask for the organization in the inspector, so one account can work with every organization the user has access to.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.appmixer.com/connector-configuration/zoho.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
