You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(zoho-desk): correct API field names, scopes, and host validation
Validation pass against Zoho's published Desk API surfaced six defects that
typecheck, lint, and the existing suite all passed over, because each one fails
silently against the live API rather than erroring.
Wire-name mismatches (Zoho ignores unknown keys, so all three were silent):
- update_ticket sent `customFields`; the ticket PATCH body names it `cf`.
`customFields` exists only as a deprecated alias on other Desk resources and
on the separate validate-field-updates endpoint, so updates reported success
and applied nothing.
- ZOHO_DESK_TICKET_PROPERTIES and ZOHO_DESK_CONTACT_PROPERTIES advertised a
`customFields` output; both resources return `cf`. The declared field always
resolved undefined and the real one was undeclared.
- list_tickets sent `departmentId`; the query param is `departmentIds`, so the
department filter was dropped and every department's tickets came back.
Content handling:
- deriveZohoContentText matched `contentType === 'html'`, but Zoho spells the
discriminator per resource: comments use `html`, threads use the MIME form
`text/html`. Every thread's `contentText` was therefore raw markup - the exact
opposite of the field's purpose. Now normalized across both spellings,
parameterized values, and casing, with regression tests.
Scopes (least privilege):
- Desk.tickets.ALL -> Desk.tickets.READ + Desk.tickets.UPDATE. No tool creates
or deletes a ticket; ALL additionally granted ticket DELETE.
- Dropped Desk.search.READ (no search tool exists) and Desk.webhooks.READ /
.UPDATE (the provider only creates and deletes), plus their orphaned
SCOPE_DESCRIPTIONS entries.
Host validation - the webhook provider was the only token-carrying path not
anchored to the Zoho apex allowlist, including the JWKS fetch, where an
unrecognized host would have stood in as the JWT issuer:
- createSubscription, deleteSubscription, and verifyAuth now route their base
through a shared allowlist check.
- getZohoDeskApiBase validates rather than trusting injection precedence.
- The organizations route uses secureFetchWithValidation with
stripAuthOnRedirect, matching the attachment route it had diverged from.
Block and trigger:
- The trigger's department field is renamed `triggerDepartmentIds`; sharing the
`departmentIds` id let a value typed as a list_tickets filter become the
webhook subscription's filter when switching modes.
- `isPublic` no longer serializes onto all ten operations, matching the existing
gating for `contentType`.
- from/limit reject negatives and fractions instead of forwarding them.
- update_ticket gains description, resolution, and classification (all already
declared as outputs), and a departmentId input so a ticket can be moved.
Accuracy corrections to user-facing text, all against the published parameter
tables: `from` is 0-based (0-4999, default 0), not 1-based; per-endpoint limits
are tickets 1-100/10, comments 1-100/50, threads 1-200/100; sortBy lists Zoho's
actual allowed values; the two `include` sets genuinely differ per endpoint;
status and priority accept comma-separated lists.
Also: path IDs are trimmed via requireZohoDeskId so a pasted trailing space
fails with a clear message instead of a %20 404; comment `commenter` and thread
`status`/`isDescriptionThread`/`visibility`/`canReply` are now declared;
ZOHO_CLIENT_ID/SECRET added to the oauth test env; docs page gains a
MANUAL-CONTENT intro covering capabilities, the Professional-edition webhook
requirement, and the US-data-center limitation.
Not verified from documentation, needs a live account before merge:
- the OAuth scope for the attachment content sub-path (Zoho publishes none, and
there is an unanswered SCOPE_MISMATCH report against it)
- 12 of the 17 offered webhook event ids (5 are confirmed); Ticket_Delete is
documented but not offered
- the ticket `descriptionContentType` key, and the POST /api/v1/webhooks body
shape, neither of which appears in any reachable Zoho reference
Copy file name to clipboardExpand all lines: apps/docs/content/docs/en/integrations/zoho_desk.mdx
+87-23Lines changed: 87 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,31 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
10
10
color="#E42527"
11
11
/>
12
12
13
+
{/* MANUAL-CONTENT-START:intro */}
14
+
[Zoho Desk](https://www.zoho.com/desk/) is Zoho's customer support help desk. Support teams use it to receive tickets from email, web forms, chat, phone, and social channels, route them to the right department and agent, and track every customer conversation through to resolution.
15
+
16
+
With the Sim Zoho Desk integration, you can:
17
+
18
+
-**Read and filter tickets**: List tickets across an organization filtered by department, status, or priority, or fetch a single ticket by ID with its related contact, assignee, and department.
19
+
-**Update tickets**: Change subject, status, priority, assignee, department, category, due date, and custom fields — useful for AI triage that classifies an incoming ticket and writes the result back.
20
+
-**Work with conversations**: List and read ticket threads (the customer-facing email/chat exchange) and comments (internal agent notes), then add your own comment as public or private.
21
+
-**Look up contacts**: Retrieve the contact behind a ticket to enrich it with data from your CRM or knowledge base.
22
+
-**Download attachments**: Pull an attachment from a thread or comment into a Sim file you can pass to downstream blocks.
23
+
-**Trigger on events**: Start a workflow when a ticket, comment, thread, contact, agent, task, or article changes in Zoho Desk.
24
+
25
+
**How it works in Sim:**
26
+
Add a Zoho Desk block to your workflow, connect your Zoho account, and pick the Organization (portal) to work in — Sim loads the list for you from the connected account. Choose an operation and fill in its parameters; the block calls the Zoho Desk API and returns structured data for downstream blocks. For comment and thread bodies, Sim adds a derived plain-text `contentText` field alongside Zoho's raw HTML `content`, so an AI agent can read the message without HTML markup.
27
+
28
+
To trigger on Zoho Desk activity instead, use the block's trigger mode. Sim creates the webhook subscription in Zoho Desk for you and removes it automatically when the workflow is undeployed.
29
+
30
+
**Requirements and limitations**
31
+
32
+
> Zoho Desk webhooks require a Zoho Desk edition of **Professional or higher** — Free and Standard plans cannot create webhook subscriptions, so the trigger will fail to deploy on those plans.
33
+
>
34
+
> Sim currently supports Zoho accounts hosted in the **US data center** (`accounts.zoho.com`). Accounts in the EU, India, Australia, or Japan data centers cannot be connected yet.
35
+
{/* MANUAL-CONTENT-END */}
36
+
37
+
13
38
## Usage Instructions
14
39
15
40
Read and update Zoho Desk tickets, manage comments and threads, look up contacts, and download attachments. Can also trigger workflows from Zoho Desk webhook events.
@@ -20,21 +45,21 @@ Read and update Zoho Desk tickets, manage comments and threads, look up contacts
20
45
21
46
### `zoho_desk_list_tickets`
22
47
23
-
List tickets from a Zoho Desk organization with optional filters.
48
+
List tickets from a Zoho Desk organization with optional filters. Returns a list projection: description, resolution, statusType and classification are only available from Get Ticket.
24
49
25
50
#### Input
26
51
27
52
| Parameter | Type | Required | Description |
28
53
| --------- | ---- | -------- | ----------- |
29
54
|`apiDomain`| string | No | Zoho Desk data-center REST base URL |
30
55
|`orgId`| string | Yes | Zoho Desk organization ID |
31
-
|`from`| number | No | Pagination start index \(1-based\)|
32
-
|`limit`| number | No | Number of tickets to return \(max 100\)|
33
-
|`departmentId`| string | No | Filter by department ID |
34
-
|`status`| string | No | Filter by status\(e.g. Open, Closed\)|
35
-
|`priority`| string | No | Filter by priority\(e.g. High\)|
36
-
|`sortBy`| string | No | Sort field\(e.g. createdTime, -modifiedTime\)|
37
-
|`include`| string | No | Comma-separated related data \(contacts, assignee, departments, team, products\)|
56
+
|`from`| number | No | Pagination start index \(0-based, max 4999\)|
57
+
|`limit`| number | No | Number of tickets to return \(1-100, default 10\)|
58
+
|`departmentIds`| string | No | Filter by department ID\(comma-separated for multiple\)|
59
+
|`status`| string | No | Filter by status, including custom statuses. Comma-separate to match multiple \(e.g. "Open,On Hold"\)|
60
+
|`priority`| string | No | Filter by priority. Comma-separate to match multiple \(e.g. "High,Urgent"\)|
61
+
|`sortBy`| string | No | Sort field: createdTime, customerResponseTime, or responseDueDate. Prefix with - for descending.|
62
+
|`include`| string | No | Comma-separated related data to embed. Allowed: contacts, products, departments, team, isRead, assignee|
38
63
39
64
#### Output
40
65
@@ -71,7 +96,7 @@ List tickets from a Zoho Desk organization with optional filters.
71
96
| ↳ `isEscalated`| boolean | Whether the ticket is escalated |
72
97
| ↳ `isOverDue`| boolean | Whether the ticket is overdue |
73
98
| ↳ `isSpam`| boolean | Whether the ticket is marked spam |
74
-
| ↳ `customFields`| json | Custom field values |
99
+
| ↳ `cf`| json | Custom field values, keyed by custom field API name|
75
100
|`count`| number | Number of tickets returned |
76
101
77
102
### `zoho_desk_get_ticket`
@@ -85,7 +110,7 @@ Retrieve a single Zoho Desk ticket by ID.
85
110
|`apiDomain`| string | No | Zoho Desk data-center REST base URL |
86
111
|`orgId`| string | Yes | Zoho Desk organization ID |
87
112
|`ticketId`| string | Yes | Ticket ID to retrieve |
88
-
|`include`| string | No | Comma-separated related data \(contacts, assignee, departments, team, products\)|
113
+
|`include`| string | No | Comma-separated related data to embed. Allowed: contacts, products, assignee, departments, contract, isRead, team, skills|
89
114
90
115
#### Output
91
116
@@ -122,7 +147,7 @@ Retrieve a single Zoho Desk ticket by ID.
122
147
| ↳ `isEscalated`| boolean | Whether the ticket is escalated |
123
148
| ↳ `isOverDue`| boolean | Whether the ticket is overdue |
124
149
| ↳ `isSpam`| boolean | Whether the ticket is marked spam |
125
-
| ↳ `customFields`| json | Custom field values |
150
+
| ↳ `cf`| json | Custom field values, keyed by custom field API name|
126
151
127
152
### `zoho_desk_update_ticket`
128
153
@@ -143,7 +168,10 @@ Update fields on an existing Zoho Desk ticket.
143
168
|`category`| string | No | Ticket category |
144
169
|`subCategory`| string | No | Ticket sub-category |
145
170
|`dueDate`| string | No | Due date \(ISO 8601\)|
146
-
|`customFields`| json | No | Custom field values as a JSON object |
171
+
|`description`| string | No | Ticket description |
172
+
|`resolution`| string | No | Resolution notes recorded on the ticket |
173
+
|`classification`| string | No | Ticket classification: Problem, Request, Question, or Others |
174
+
|`customFields`| json | No | Custom field values as a JSON object, keyed by custom field API name |
147
175
148
176
#### Output
149
177
@@ -180,7 +208,7 @@ Update fields on an existing Zoho Desk ticket.
180
208
| ↳ `isEscalated`| boolean | Whether the ticket is escalated |
181
209
| ↳ `isOverDue`| boolean | Whether the ticket is overdue |
182
210
| ↳ `isSpam`| boolean | Whether the ticket is marked spam |
183
-
| ↳ `customFields`| json | Custom field values |
211
+
| ↳ `cf`| json | Custom field values, keyed by custom field API name|
184
212
185
213
### `zoho_desk_list_comments`
186
214
@@ -193,8 +221,8 @@ List comments on a Zoho Desk ticket.
193
221
|`apiDomain`| string | No | Zoho Desk data-center REST base URL |
194
222
|`orgId`| string | Yes | Zoho Desk organization ID |
195
223
|`ticketId`| string | Yes | Ticket ID |
196
-
|`from`| number | No | Pagination start index \(1-based\)|
197
-
|`limit`| number | No | Number of comments to return \(max 100\)|
224
+
|`from`| number | No | Pagination start index \(0-based\)|
225
+
|`limit`| number | No | Number of comments to return \(1-100, default 50\)|
198
226
199
227
#### Output
200
228
@@ -207,6 +235,14 @@ List comments on a Zoho Desk ticket.
207
235
| ↳ `contentText`| string | Plain-text rendering of content \(HTML stripped when contentType is html\)|
208
236
| ↳ `isPublic`| boolean | Whether the comment is public |
209
237
| ↳ `commenterId`| string | Commenter ID |
238
+
| ↳ `commenter`| object | Who wrote the comment |
239
+
| ↳ `name`| string | Display name |
240
+
| ↳ `firstName`| string | First name |
241
+
| ↳ `lastName`| string | Last name |
242
+
| ↳ `email`| string | Email address |
243
+
| ↳ `type`| string | Commenter type \(AGENT/END_USER\)|
@@ -228,7 +264,7 @@ Add a comment to a Zoho Desk ticket.
228
264
|`orgId`| string | Yes | Zoho Desk organization ID |
229
265
|`ticketId`| string | Yes | Ticket ID |
230
266
|`content`| string | Yes | Comment content |
231
-
|`contentType`| string | No | Content type: plainText or html |
267
+
|`contentType`| string | No | Content type: plainText or html. Defaults to plainText so agent-written text posts literally; pass 'html' to send markup \(Zoho's own API default is html\).|
232
268
|`isPublic`| boolean | No | Whether the comment is public |
233
269
234
270
#### Output
@@ -242,6 +278,14 @@ Add a comment to a Zoho Desk ticket.
242
278
| ↳ `contentText`| string | Plain-text rendering of content \(HTML stripped when contentType is html\)|
243
279
| ↳ `isPublic`| boolean | Whether the comment is public |
244
280
| ↳ `commenterId`| string | Commenter ID |
281
+
| ↳ `commenter`| object | Who wrote the comment |
282
+
| ↳ `name`| string | Display name |
283
+
| ↳ `firstName`| string | First name |
284
+
| ↳ `lastName`| string | Last name |
285
+
| ↳ `email`| string | Email address |
286
+
| ↳ `type`| string | Commenter type \(AGENT/END_USER\)|
0 commit comments