Skip to content

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:

  1. Setup → Developer Space → API
  2. 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:

  1. Setup → General → Departments → Edit the department you want.
  2. 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.

  1. 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).
  2. Add ClientSelf Client.
  3. 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:

  1. Click the Generate Code tab.
  2. Scope (paste exactly):

    Desk.tickets.ALL,Desk.basic.READ,Desk.contacts.READ
    

    Want read-only investigation (no auto-reply)? Use this narrower set:

    Desk.tickets.READ,Desk.basic.READ,Desk.contacts.READ
    
  3. Time Duration: leave the default (10 min). The code is one-time; you only need it long enough to paste into Gaby.

  4. Scope Description: anything (e.g. gaby-prod); free text.
  5. Click Create. You get a code that looks like 1000.abc…xyz.
  6. 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:

  1. Onboarding step 2 (first-run) or Connectors → Add → Zoho Desk.
  2. Fill the form with the values gathered above.
  3. 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:

    ticket_poller_started source_id=... kind=zoho_desk interval_s=...
    
  • 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.