Zoho Desk (ticket source)¶
Gaby polls Zoho Desk for new tickets and replies back in-thread. This page walks you through getting the five pieces of information Gaby needs:
| Field | Where it comes from |
|---|---|
region |
Your Zoho datacentre suffix (com, eu, in, com.au, jp, com.cn) |
org_id |
Zoho Desk → Setup → Developer Space → API → Organization ID |
client_id |
Zoho API Console → Self Client → Client ID |
client_secret |
Zoho API Console → Self Client → Client Secret |
code |
Zoho API Console → Self Client → Generate Code (one-time, expires in 10 min) |
department_id (optional) |
URL when viewing a department in Zoho Desk |
You paste the code exactly once. Gaby exchanges it server-side for a
refresh token, stores it envelope-encrypted, and uses it to mint
short-lived access tokens forever after, no further clicking required.
1. Identify your region¶
Look at the URL you log into Zoho Desk with:
| Zoho domain you see | region value |
|---|---|
desk.zoho.com |
com |
desk.zoho.eu |
eu |
desk.zoho.in |
in |
desk.zoho.com.au |
com.au |
desk.zoho.jp |
jp |
desk.zoho.com.cn |
com.cn |
This determines which datacentre Gaby calls. The API Console (next step) is
region-specific too: most people use https://api-console.zoho.com/
(US/global). If your Desk URL has a different suffix (.eu, .in,
.com.au, .jp, .com.cn), use the matching api-console.zoho.<region>.
Cross-region clients fail with OAUTH_REQUEST_DENIED.
2. Find your Organization ID¶
In Zoho Desk:
- Setup → Developer Space → API
- The Organization ID is the long number near the top.
Alternative: it also appears in the URL when you're viewing org-level
settings, after ?org= or in API responses as id on the /organizations
endpoint.
Optional: poll a single department
Set department_id only if you want Gaby to poll one department
instead of all. In Zoho Desk:
- Setup → General → Departments → Edit the department you want.
- The URL contains a numeric
departmentId=…segment. That's the value.
Leave blank to poll across every department your role can see.
3. Create a Self Client in the API Console¶
A Self Client is Zoho's term for a server-to-server OAuth app (no redirect URI, no user interaction at runtime). Perfect for Gaby.
- Go to https://api-console.zoho.com/ (or
api-console.zoho.<region>if your Desk isn't on the US/global datacentre, see step 1). - Add Client → Self Client.
- You get a Client ID and Client Secret. Copy both, you'll paste them into Gaby.
These are long-lived; you don't need to regenerate them unless you rotate the secret.
4. Generate the one-time code¶
Still inside that Self Client:
- Click the Generate Code tab.
-
Scope (paste exactly):
Want read-only investigation (no auto-reply)? Use this narrower set:
-
Time Duration: leave the default (10 min). The code is one-time; you only need it long enough to paste into Gaby.
- Scope Description: anything (e.g.
gaby-prod); free text. - Click Create. You get a code that looks like
1000.abc…xyz. - Copy it immediately and move to the next step. It expires in 10 minutes and can only be exchanged once.
Don't refresh the page yet
The code only displays once. Refreshing the API Console hides it. If you lose it, just hit Generate Code again, no real harm done.
5. Paste into Gaby¶
In the Gaby web UI:
- Onboarding step 2 (first-run) or Connectors → Add → Zoho Desk.
- Fill the form with the values gathered above.
- Save.
Behind the scenes Gaby calls Zoho's OAuth token endpoint with your
client_id + client_secret + code, receives a refresh_token,
encrypts it at rest, and from then on auto-refreshes the access token
before every call. The code itself is discarded after the exchange.
6. Verify the connection¶
After save:
- The ticket source row turns ready. (degraded means an API call failed: check region + org_id + scopes.)
-
Backend logs show:
-
If you have queued tickets, the investigation consumer picks them up and posts back through the same connector.
That's the whole setup. The sections below are reference — open them only if you need them.
Scope reference
| Scope | Why Gaby needs it |
|---|---|
Desk.tickets.READ |
Poll for new tickets, read conversation history |
Desk.tickets.UPDATE (part of Desk.tickets.ALL) |
Mark tickets resolved after auto-reply |
Desk.tickets.CREATE (part of Desk.tickets.ALL) |
Post replies in-thread |
Desk.basic.READ |
Healthcheck against /api/v1/organizations |
Desk.contacts.READ |
Surface contact details to the agent loop |
For investigate-only (read-only autonomy), Desk.tickets.READ is
sufficient and Gaby will not attempt any writes.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
OAUTH_REQUEST_DENIED on save |
Client was created in the wrong region's API Console | Recreate in api-console.zoho.<region> matching your Desk region |
INVALID_CODE |
Code expired (>10 min) or already used once | Generate a fresh code in the Self Client and re-save |
| 401 / refresh failures after working previously | Refresh token revoked (e.g., admin reset, scope change) | Generate a new code, re-save the source. The old refresh token is replaced on save. |
Source degraded, "no organization found" |
Wrong org_id, or Desk.basic.READ not in the granted scope |
Re-verify Org ID; regenerate code with Desk.basic.READ |
| Polling works but writeback (auto-reply) 403s | Read-only scope only | Regenerate with Desk.tickets.ALL (or at least Desk.tickets.CREATE + Desk.tickets.UPDATE) |
Zoho's own documentation
The OAuth + Self Client flow above is Gaby-specific framing of:
If the API Console UI changes, those are the source of truth; the field-by-field mapping above stays the same.