Skip to content

Commit 0ab44c5

Browse files
authored
improvement(zoho-desk): pick the data center from a dropdown and trim service-account help text (#6271)
* improvement(zoho-desk): pick the data center from a dropdown and trim service-account help text The Zoho Desk Self Client modal rendered a paragraph of setup steps as the hint under Client secret, duplicating both the setup guide and two of its own field hints. Cut it to the one caveat that isn't derivable from the form, and moved it to the org-identifier field the caveats actually qualify. Data center is now a dropdown sourced from ZOHO_DESK_DATA_CENTERS. Same editorial pass across the other service accounts: Zoom, Salesforce, Shopify, Webflow, Trello and Cal.com dropped setup steps in favor of caveats. Also adds the documented Zoho Desk params that were missing (list_tickets assignee/channel/receivedInDays, list_comments and list_threads sortBy, get_contact and get_thread include), each gated per operation so a stale subBlock value can't leak into an endpoint that reads the same param name. * fix(zoho-desk): let an unsupported receivedInDays reach the tool's validation The block mapper filtered on shape before forwarding, so a fractional or non-numeric value was dropped and List Tickets then ran with no window at all — returning the whole queue as though the requested filter had applied. The tool owns that validation, so the mapper now passes the value straight through. Adds a block-to-tool seam test: neither side's own tests could catch a value lost between them. * fix(zoho-desk): overwrite operation-scoped params instead of omitting them The block mapper scoped params by destructuring them out of the spread, on the assumption that a key left out of the return value never reaches the tool. It does: both call sites merge the mapper's output on top of the original inputs (`{ ...inputs, ...transformedParams }`), so an omitted key is restored. The serializer is what actually held this together, and it has a gap — an advanced subBlock with a retained value is emitted for every operation while the block's advanced toggle is off, because that branch returns on isNonEmptyValue without evaluating the subBlock's condition. So a Sort By set on List Tickets reached List Comments, and a ticket Include reached Get Contact, each rejected by Zoho. Out-of-range from/limit reached the wire for the same reason. Every scoped param is now assigned unconditionally, undefined included, so the merge cannot resurrect a stale value. Also fixes a crash this branch introduced: clearing the Departments multi-select stores [], which reached the comma-list normalizer and threw on .split. The helper now takes arrays, which is what that subBlock actually stores. The block-to-tool tests now model the real merge rather than the mapper's return value alone — the previous version passed while production threw on the same input. Corrects two comments that misstated where Zoho documents customFields and errorMessage, and splits the shared include subBlock, since Get Ticket accepts contract and skills and List Tickets does not. * fix(zoho-desk): do not scope params on the agent-tool path The previous commit made the mapper assign every operation-scoped param unconditionally, so the merge could not resurrect a stale value. That is right on the canvas path and wrong on the agent-tool path, where `operation` is a sibling of the tool call rather than a member of params: the mapper saw `operation === undefined`, every gate resolved to undefined, and the merge then overwrote the model's own arguments with it. A Zoho Desk tool called by an agent lost every parameter the model supplied. That path needs no scoping — the tool is already chosen, and the model addresses tool params by their real names — so it now returns early. Custom fields are still coerced there, since parsing JSON is a type fix rather than an operation gate, and that parsing is now shared by both paths. * fix(zoho-desk): keep the legacy include working on Get Ticket Splitting the shared `include` subBlock into `include` and `ticketInclude` left workflows saved before the split reading an empty field, so their Get Ticket calls silently stopped embedding what they asked for. Get Ticket now reads `ticketInclude ?? include`. The fallback only goes that direction: Get Ticket accepts every value List Tickets does plus `contract` and `skills`, so a legacy value is always valid there, while List Tickets still reads only `include` and can never receive the two extra tokens it does not document.
1 parent fbd02bc commit 0ab44c5

20 files changed

Lines changed: 869 additions & 135 deletions

File tree

apps/docs/content/docs/en/integrations/zoho-desk-service-account.mdx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ This is the recommended way to use Zoho Desk in production workflows: the creden
1313

1414
## Prerequisites
1515

16-
You need a Zoho account with access to the [Zoho API Console](https://api-console.zoho.com) for the same organization your Zoho Desk portal belongs to, and the Zoho Desk **organization ID** for that portal.
16+
You need a Zoho account with access to the Zoho API Console **for your data center**, for the same organization your Zoho Desk portal belongs to, and the Zoho Desk **organization ID** for that portal. See [Know Your Data Center](#3-know-your-data-center) for the console that matches your region.
1717

1818
<Callout type="info">
19-
A Self Client can authenticate against the **US, EU, IN, or AU** accounts server — pick your region with the **Data center** field when you add the credential, or leave it blank for US. Organizations in the JP, CA, SA, CN, or UK data centers are not supported yet. API calls are then routed to the Desk host for that same region, so data residency is honored end to end.
19+
A Self Client can authenticate against the **US, EU, IN, or AU** accounts server — pick your region from the **Data center** dropdown when you add the credential, or leave it unset for US. Organizations in the JP, CA, SA, CN, or UK data centers are not supported yet. API calls are then routed to the Desk host for that same region, so data residency is honored end to end.
2020

2121
The interactive **OAuth** connection is a separate path and remains **US-only** (`accounts.zoho.com`), so a non-US organization can connect only through a Self Client.
2222
</Callout>
@@ -31,7 +31,7 @@ Zoho Desk **webhooks are a Professional-edition and above feature**. The Zoho De
3131

3232
<Steps>
3333
<Step>
34-
Sign in at [api-console.zoho.com](https://api-console.zoho.com) with the Zoho account that owns the Desk portal
34+
Sign in to the Zoho API Console **for your data center** with the Zoho account that owns the Desk portal — see [Know Your Data Center](#3-know-your-data-center) for the right one. A Self Client is registered in one data center and cannot authenticate against another region's accounts server
3535
</Step>
3636
<Step>
3737
Click **Add Client**, choose **Self Client**, and click **Create** — then **OK** on the confirmation
@@ -68,20 +68,24 @@ This must be the organization ID of the Desk portal you want the workflows to ac
6868

6969
### 3. Know Your Data Center
7070

71-
Zoho hosts each organization in one data center, and the accounts server that issues tokens is per region. Look at the URL you use to sign in to Zoho Desk and pick the matching code:
71+
Zoho hosts each organization in one data center, and the accounts server that issues tokens is per region. Look at the URL you use to sign in to Zoho Desk and pick the matching region in the **Data center** dropdown:
7272

73-
| Data center | Sign-in domain | Code to enter |
73+
| Sign-in domain | Data center to pick | API Console to create the Self Client in |
7474
| --- | --- | --- |
75-
| United States | `zoho.com` | `us` (or leave blank) |
76-
| Europe | `zoho.eu` | `eu` |
77-
| India | `zoho.in` | `in` |
78-
| Australia | `zoho.com.au` | `au` |
75+
| `zoho.com` | United States (the default when left unset) | [api-console.zoho.com](https://api-console.zoho.com) |
76+
| `zoho.eu` | Europe | [api-console.zoho.eu](https://api-console.zoho.eu) |
77+
| `zoho.in` | India | [api-console.zoho.in](https://api-console.zoho.in) |
78+
| `zoho.com.au` | Australia | [api-console.zoho.com.au](https://api-console.zoho.com.au) |
7979

8080
Organizations in the JP, CA, SA, CN, and UK data centers cannot be connected yet.
8181

82+
<Callout type="warn">
83+
Create the Self Client in the console for **your** data center. Zoho ties a client to the region it was registered in — *"the accounts-server-url is specific to the location (i.e., datacenter) where the client is registered"* — and the multi-data-center setting that would extend a client to other regions is [not available for Self Clients](https://docs.catalyst.zoho.com/en/api/oauth2/register-new-client/). A Self Client created in the wrong console cannot be repointed later; create a new one in the right region.
84+
</Callout>
85+
8286
### 4. Scopes
8387

84-
Sim requests exactly the scopes its Zoho Desk tools and trigger exercise:
88+
Sim requests the Zoho Desk scopes its tools and its webhook trigger exercise — the same list on both connection types:
8589

8690
```
8791
Desk.tickets.READ
@@ -91,10 +95,11 @@ Desk.agents.READ
9195
Desk.basic.READ
9296
Desk.webhooks.CREATE
9397
Desk.webhooks.DELETE
94-
aaaserver.profile.READ
9598
```
9699

97-
Sim sends this list on every token request, so there is nothing to pre-configure on the Self Client itself. If Zoho rejects the request with an invalid-scope error, the Self Client's owner does not have access to one of the Desk modules above in that organization.
100+
Sim sends this list on every token request, so there is nothing to pre-configure on the Self Client itself. The `aaaserver.profile.READ` scope Sim requests on the interactive OAuth flow is deliberately left off this grant — it is an Accounts *profile* scope, and this grant never calls the Accounts profile endpoint; identity is synthesized from the organization ID. If Zoho rejects the request with an invalid-scope error, the Self Client's owner does not have access to one of the Desk modules above in that organization.
101+
102+
`Desk.webhooks.CREATE` and `Desk.webhooks.DELETE` belong to the trigger, which runs on an OAuth connection only (see [Triggers](#triggers-still-need-oauth) below). They are harmless on a Self Client grant, but a Free or Standard Desk plan may reject them, since webhooks are a Professional-edition feature.
98103

99104
A scope that is granted but insufficient surfaces at run time as a `4xx` from the Zoho Desk API naming the scope problem.
100105

@@ -118,7 +123,7 @@ Regenerating or revoking the Self Client in the Zoho API Console invalidates the
118123
{/* TODO(screenshot): Zoho Desk integration page with the Add Self Client connect option */}
119124
</Step>
120125
<Step>
121-
In the **Add Zoho Desk Self Client** dialog, paste the **Client ID**, the **Client secret**, and the numeric **Organization ID**. Set **Data center** to your region (`us`, `eu`, `in`, or `au`) — leave it blank for US. Optionally set a display name and description
126+
In the **Add Zoho Desk Self Client** dialog, paste the **Client ID**, the **Client secret**, and the numeric **Organization ID**. Pick your region from the **Data center** dropdown — leaving it unset uses the United States. Optionally set a display name and description
122127

123128
{/* TODO(screenshot): Add Zoho Desk Self Client dialog with all fields filled in */}
124129
</Step>
@@ -153,10 +158,10 @@ Access tokens minted from a Self Client live for one hour and there is **no refr
153158
<FAQ items={[
154159
{ question: "Why a Self Client instead of OAuth?", answer: "A Self Client authenticates as your Zoho organization, not as a person — nothing expires when someone leaves or their login lapses. Sim mints short-lived tokens from the stored client ID and secret whenever a workflow runs." },
155160
{ question: "Where do I find the Organization ID?", answer: "In Zoho Desk, go to Setup (gear icon) → Developer Space → API. The numeric Organization ID shown there is the value to paste. This is expected to be the same ID that Zoho Desk API calls send in the orgId header; if Zoho rejects it with missing_org_info, paste the full ZohoDesk.<your-org-id> value instead." },
156-
{ question: "Zoho rejects my credentials with invalid_client — why?", answer: "Either the client ID or secret was mistyped, or the client you created is not a Self Client. Only Self Clients support the client-credentials grant — in the Zoho API Console, Add Client → Self Client. Copy both values from the client's Client Secret tab." },
161+
{ question: "Zoho rejects my credentials with invalid_client — why?", answer: "Either the client ID or secret was mistyped, the client you created is not a Self Client, or the Self Client was created in a different data center's API Console than the Data center you selected. Only Self Clients support the client-credentials grant — in the Zoho API Console, Add Client → Self Client. Copy both values from the client's Client Secret tab, and create the client in the console for your region." },
157162
{ question: "Zoho returns missing_org_info or rejects the organization — why?", answer: "Zoho could not resolve a Desk organization from the ID you pasted. Re-copy the numeric Organization ID from Setup → Developer Space → API in the Desk portal you want to use. If your Zoho account has multiple Desk portals, make sure it is the ID of the right one." },
158-
{ question: "Can I use a Self Client with a non-US Zoho account?", answer: "Yes, for the US, EU, IN, and AU data centers. Set the Data center field to us, eu, in, or au when you add the credential, and Sim mints tokens against that region's accounts server and calls the Desk host in the same region. Leaving it blank means US. The JP, CA, SA, CN, and UK data centers are not supported yet, and the interactive OAuth connection remains US-only." },
159-
{ question: "I picked the wrong data center — what happens?", answer: "The region's accounts server does not know your organization, so Zoho rejects the token request and Sim reports that it could not authenticate. Edit the credential and set the Data center to the region whose domain you sign in to Zoho Desk with." },
163+
{ question: "Can I use a Self Client with a non-US Zoho account?", answer: "Yes, for the US, EU, IN, and AU data centers. Pick your region from the Data center dropdown when you add the credential, and Sim mints tokens against that region's accounts server and calls the Desk host in the same region. Leaving it unset means US. Create the Self Client in that region's API Console too. The JP, CA, SA, CN, and UK data centers are not supported yet, and the interactive OAuth connection remains US-only." },
164+
{ question: "I picked the wrong data center — what happens?", answer: "The region's accounts server does not know your Self Client, so Zoho rejects the token request and Sim reports that it could not authenticate. Edit the credential and pick the region whose domain you sign in to Zoho Desk with — and check that the Self Client itself was created in that same region's API Console, since a client cannot authenticate against another region." },
160165
{ question: "Why doesn't my Zoho Desk trigger work with the Self Client?", answer: "The trigger provisions a webhook subscription in your Desk organization, and that path runs against a personal OAuth connection only. Connect Zoho Desk through OAuth for triggers. Separately, Zoho Desk webhooks require a Professional-edition plan or above — they are unavailable on Free and Standard." },
161166
{ question: "How do I rotate the credentials?", answer: "Regenerate the client secret on the Self Client's Client Secret tab in the Zoho API Console, then update the credential in Sim with the new secret. The old secret stops working as soon as it's regenerated, so update Sim promptly." },
162167
]} />

apps/docs/content/docs/en/integrations/zoho_desk.mdx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ List tickets from a Zoho Desk organization with optional filters. Returns a list
5353
| --------- | ---- | -------- | ----------- |
5454
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
5555
| `orgId` | string | Yes | Zoho Desk organization ID |
56-
| `from` | number | No | Pagination start index \(0-based, max 4999\) |
57-
| `limit` | number | No | Number of tickets to return \(1-100, default 10\) |
56+
| `from` | number | No | Pagination start index \(0-based\) |
57+
| `limit` | number | No | Number of tickets to return \(1-100\) |
5858
| `departmentIds` | string | No | Filter by department ID \(comma-separated for multiple\) |
5959
| `status` | string | No | Filter by status, including custom statuses. Comma-separate to match multiple \(e.g. "Open,On Hold"\) |
6060
| `priority` | string | No | Filter by priority. Comma-separate to match multiple \(e.g. "High,Urgent"\) |
61+
| `assignee` | string | No | Filter by assignee: an agent ID, or "Unassigned". Comma-separate to match multiple. |
62+
| `channel` | string | No | Filter by origin channel, spelled as your portal spells it. Comma-separate to match multiple. |
63+
| `receivedInDays` | number | No | Only tickets whose last customer response was within the last 15, 30, or 90 days \(Zoho filters on customerResponseTime, despite the name\) |
6164
| `sortBy` | string | No | Sort field: createdTime, customerResponseTime, or responseDueDate. Prefix with - for descending. |
6265
| `include` | string | No | Comma-separated related data to embed. Allowed: contacts, products, departments, team, isRead, assignee |
6366

@@ -88,6 +91,7 @@ List tickets from a Zoho Desk organization with optional filters. Returns a list
8891
|`responseDueDate` | string | Response due date |
8992
|`createdTime` | string | Created timestamp |
9093
|`modifiedTime` | string | Last modified timestamp |
94+
|`customerResponseTime` | string | Time the last customer response was received |
9195
|`closedTime` | string | Closed timestamp |
9296
|`resolution` | string | Resolution text |
9397
|`threadCount` | string | Number of threads |
@@ -139,6 +143,7 @@ Retrieve a single Zoho Desk ticket by ID.
139143
|`responseDueDate` | string | Response due date |
140144
|`createdTime` | string | Created timestamp |
141145
|`modifiedTime` | string | Last modified timestamp |
146+
|`customerResponseTime` | string | Time the last customer response was received |
142147
|`closedTime` | string | Closed timestamp |
143148
|`resolution` | string | Resolution text |
144149
|`threadCount` | string | Number of threads |
@@ -170,7 +175,7 @@ Update fields on an existing Zoho Desk ticket.
170175
| `dueDate` | string | No | Due date \(ISO 8601\) |
171176
| `description` | string | No | Ticket description |
172177
| `resolution` | string | No | Resolution notes recorded on the ticket |
173-
| `classification` | string | No | Ticket classification: Problem, Request, Question, or Others |
178+
| `classification` | string | No | Ticket classification. Zoho\'s system-defined values are Problem, Request, and Question; portals can define custom values. Pass "" to clear it. |
174179
| `customFields` | json | No | Custom field values as a JSON object, keyed by custom field API name |
175180

176181
#### Output
@@ -200,6 +205,7 @@ Update fields on an existing Zoho Desk ticket.
200205
|`responseDueDate` | string | Response due date |
201206
|`createdTime` | string | Created timestamp |
202207
|`modifiedTime` | string | Last modified timestamp |
208+
|`customerResponseTime` | string | Time the last customer response was received |
203209
|`closedTime` | string | Closed timestamp |
204210
|`resolution` | string | Resolution text |
205211
|`threadCount` | string | Number of threads |
@@ -223,6 +229,7 @@ List comments on a Zoho Desk ticket.
223229
| `ticketId` | string | Yes | Ticket ID |
224230
| `from` | number | No | Pagination start index \(0-based\) |
225231
| `limit` | number | No | Number of comments to return \(1-100, default 50\) |
232+
| `sortBy` | string | No | Sort by commentedTime. Ascending by default; prefix with - for descending \(-commentedTime\). |
226233

227234
#### Output
228235

@@ -307,6 +314,7 @@ List conversation threads on a Zoho Desk ticket, newest first (Zoho sorts by sen
307314
| `ticketId` | string | Yes | Ticket ID |
308315
| `from` | number | No | Pagination start index \(0-based\) |
309316
| `limit` | number | No | Number of threads to return \(1-200, default 100\) |
317+
| `sortBy` | string | No | Sort by sendDateTime. Zoho sorts descending \(newest first\) when unset; pass sendDateTime for oldest first. |
310318

311319
#### Output
312320

@@ -333,7 +341,7 @@ List conversation threads on a Zoho Desk ticket, newest first (Zoho sorts by sen
333341
|`isContentTruncated` | boolean | Whether Zoho truncated the thread content; fetch fullContentURL for the rest |
334342
|`fullContentURL` | string | URL returning the untruncated thread content |
335343
|`plainText` | string | Zoho's own plain-text rendering of the thread, when it supplies one |
336-
|`status` | string | Delivery status of an outgoing thread \(SUCCESS/FAILED/DRAFT\) |
344+
|`status` | string | Delivery status of the thread \(e.g. SUCCESS, PENDING, FAILED, DRAFT\) |
337345
|`isDescriptionThread` | boolean | Whether this thread is the ticket's original description |
338346
|`visibility` | string | Thread visibility \(e.g. public\) |
339347
|`canReply` | boolean | Whether the thread can be replied to |
@@ -363,6 +371,7 @@ Retrieve the full content of a single Zoho Desk ticket thread.
363371
| `orgId` | string | Yes | Zoho Desk organization ID |
364372
| `ticketId` | string | Yes | Ticket ID |
365373
| `threadId` | string | Yes | Thread ID |
374+
| `include` | string | No | Related data to embed. Allowed: plainText — Zoho's own plain-text rendering of the thread |
366375

367376
#### Output
368377

@@ -389,7 +398,7 @@ Retrieve the full content of a single Zoho Desk ticket thread.
389398
|`isContentTruncated` | boolean | Whether Zoho truncated the thread content; fetch fullContentURL for the rest |
390399
|`fullContentURL` | string | URL returning the untruncated thread content |
391400
|`plainText` | string | Zoho's own plain-text rendering of the thread, when it supplies one |
392-
|`status` | string | Delivery status of an outgoing thread \(SUCCESS/FAILED/DRAFT\) |
401+
|`status` | string | Delivery status of the thread \(e.g. SUCCESS, PENDING, FAILED, DRAFT\) |
393402
|`isDescriptionThread` | boolean | Whether this thread is the ticket's original description |
394403
|`visibility` | string | Thread visibility \(e.g. public\) |
395404
|`canReply` | boolean | Whether the thread can be replied to |
@@ -417,6 +426,7 @@ Retrieve a Zoho Desk contact by ID.
417426
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
418427
| `orgId` | string | Yes | Zoho Desk organization ID |
419428
| `contactId` | string | Yes | Contact ID to retrieve |
429+
| `include` | string | No | Comma-separated related data to embed. Allowed: accounts, owner |
420430

421431
#### Output
422432

0 commit comments

Comments
 (0)