A comprehensive, type-safe TypeScript SDK for the Zoho Desk API with OAuth 2.0, multi-data-center support, and token persistence.
This SDK is built on four core principles:
-
Spec-driven — API clients are auto-generated from Zoho's official OpenAPI specs using Microsoft Kiota. Every endpoint, model, and parameter stays in sync with the source of truth. No hand-written API wrappers to maintain or fall out of date.
-
Type-safe by default — Strict TypeScript throughout. Builder patterns for fluent, discoverable configuration. Full autocompletion and compile-time validation so issues surface in your editor, not at runtime.
-
Batteries-included but extensible — Ships with sensible defaults (file-based token store, configurable logging, proxy support) while keeping every component pluggable. Swap in your own token store, adjust SDK behavior, or route through a corporate proxy — all through clean interfaces.
-
Modern ESM stack — ESM-only with ES2022 target and NodeNext module resolution. No CommonJS baggage, no dual-package hazards. Designed for the Node.js ecosystem as it is today.
- 126 API modules auto-generated from official Zoho Desk OpenAPI specs
- OAuth 2.0 with 7 grant types: refresh token, authorization code, client credentials, device code, implicit, direct access token, and stored token
- PKCE support (RFC 7636) for secure public client flows
- Authorization URL builder for constructing OAuth consent screens
- Device authorization flow for headless/IoT devices
- Automatic token refresh with 5-second expiry buffer
- 7-region data center support — US, EU, IN, AU, CA, CN, JP
- Pluggable token persistence with built-in CSV file store
- Structured error handling with
ZohoApiErrorandSDKException - Automatic retry with configurable max retries and delay
- HTTP proxy support via undici ProxyAgent
- Configurable logging powered by Winston
- Lazy-loaded API clients for minimal startup overhead
- TypeScript strict mode with full declaration maps
npm install @banana.inc/zoho-desk-nodejs-sdkRequires Node.js >= 18. This package is ESM-only ("type": "module").
import {
InitializeBuilder,
OAuthBuilder,
USDataCenter,
FileStore,
createDeskClient,
} from "@banana.inc/zoho-desk-nodejs-sdk";
// 1. Build the OAuth token
const token = new OAuthBuilder()
.clientId("your-client-id")
.clientSecret("your-client-secret")
.refreshToken("your-refresh-token")
.build();
// 2. Initialize the SDK
await new InitializeBuilder()
.environment(USDataCenter.PRODUCTION())
.token(token)
.store(new FileStore("./tokens.csv"))
.initialize();
// 3. Create the client and call APIs
const client = createDeskClient();
const tickets = await client.tickets.get();The SDK supports seven OAuth grant types through OAuthBuilder:
For server-side applications with long-lived access.
const token = new OAuthBuilder()
.clientId("your-client-id")
.clientSecret("your-client-secret")
.refreshToken("your-refresh-token")
.build();For exchanging a one-time authorization code for tokens.
const token = new OAuthBuilder()
.clientId("your-client-id")
.clientSecret("your-client-secret")
.grantToken("your-grant-token")
.redirectURL("https://your-app.com/callback")
.build();For quick testing or short-lived scripts where you already have an access token.
const token = new OAuthBuilder()
.accessToken("your-access-token")
.build();For server-to-server applications without user context.
const token = new OAuthBuilder()
.clientId("your-client-id")
.clientSecret("your-client-secret")
.scope("Desk.tickets.ALL")
.orgId("your-org-id")
.clientCredentials()
.build();For resuming a previously persisted token session.
const token = new OAuthBuilder()
.id("stored-token-id")
.build();Build OAuth authorization URLs for redirecting users to the Zoho consent screen.
import { AuthorizationUrlBuilder, USDataCenter } from "@banana.inc/zoho-desk-nodejs-sdk";
const authUrl = new AuthorizationUrlBuilder()
.clientId("your-client-id")
.scope("Desk.tickets.ALL")
.redirectUri("https://your-app.com/callback")
.responseType("code") // "code" (default) or "token" for implicit flow
.accessType("offline") // "offline" (default) or "online"
.state("csrf-token") // optional CSRF protection
.prompt("consent") // optional
.build(USDataCenter.PRODUCTION());Secure authorization code flows for public clients (RFC 7636).
import {
generatePKCEPair,
AuthorizationUrlBuilder,
OAuthBuilder,
USDataCenter,
} from "@banana.inc/zoho-desk-nodejs-sdk";
// Generate a PKCE pair
const pkce = await generatePKCEPair();
// Include in authorization URL
const authUrl = new AuthorizationUrlBuilder()
.clientId("your-client-id")
.scope("Desk.tickets.ALL")
.redirectUri("https://your-app.com/callback")
.pkce(pkce)
.build(USDataCenter.PRODUCTION());
// Exchange the code with the verifier
const token = new OAuthBuilder()
.clientId("your-client-id")
.clientSecret("your-client-secret")
.grantToken("authorization-code-from-callback")
.redirectURL("https://your-app.com/callback")
.codeVerifier(pkce.codeVerifier)
.build();For devices without a browser (IoT, CLI tools, smart TVs).
import {
requestDeviceCode,
pollForDeviceToken,
USDataCenter,
} from "@banana.inc/zoho-desk-nodejs-sdk";
// Step 1: Request a device code
const deviceCode = await requestDeviceCode(
USDataCenter.PRODUCTION(),
"your-client-id",
"Desk.tickets.ALL",
);
// Step 2: Show the user where to authorize
console.log(`Visit ${deviceCode.verification_url} and enter code: ${deviceCode.user_code}`);
// Step 3: Poll until the user authorizes (supports cancellation)
const controller = new AbortController();
const token = await pollForDeviceToken(
USDataCenter.PRODUCTION(),
deviceCode,
{
clientId: "your-client-id",
clientSecret: "your-client-secret",
signal: controller.signal,
onPoll: (status) => console.log(`Status: ${status}`),
},
);Parse tokens from URL fragments after an implicit OAuth redirect.
import { parseImplicitFragment } from "@banana.inc/zoho-desk-nodejs-sdk";
// Build the authorization URL with responseType("token") first,
// then parse the redirect fragment:
const fragment = "access_token=1000.abc...&expires_in=3600&token_type=Bearer";
const token = parseImplicitFragment(fragment);The SDK supports all 7 Zoho Desk data center regions:
| Region | Class | Production Environment |
|---|---|---|
| US | USDataCenter |
USDataCenter.PRODUCTION() |
| EU | EUDataCenter |
EUDataCenter.PRODUCTION() |
| IN | INDataCenter |
INDataCenter.PRODUCTION() |
| AU | AUDataCenter |
AUDataCenter.PRODUCTION() |
| CA | CADataCenter |
CADataCenter.PRODUCTION() |
| CN | CNDataCenter |
CNDataCenter.PRODUCTION() |
| JP | JPDataCenter |
JPDataCenter.PRODUCTION() |
Customize SDK behavior with SDKConfigBuilder:
import { SDKConfigBuilder } from "@banana.inc/zoho-desk-nodejs-sdk";
const config = new SDKConfigBuilder()
.autoRefreshFields(true)
.pickListValidation(true)
.timeout(30000) // request timeout in ms
.maxRetries(5) // 0–10, default 3
.retryDelay(10) // seconds, 0–180, default 3
.build();
await new InitializeBuilder()
.environment(USDataCenter.PRODUCTION())
.token(token)
.SDKConfig(config)
.initialize();Enable Winston-based logging with LogBuilder:
import { LogBuilder, Levels } from "@banana.inc/zoho-desk-nodejs-sdk";
const logger = new LogBuilder()
.level(Levels.INFO)
.filePath("./sdk.log")
.build();
await new InitializeBuilder()
.environment(USDataCenter.PRODUCTION())
.token(token)
.logger(logger)
.initialize();Available levels: INFO, DEBUG, WARN, VERBOSE, ERROR, SILLY, OFF
Route requests through a proxy with ProxyBuilder:
import { ProxyBuilder } from "@banana.inc/zoho-desk-nodejs-sdk";
const proxy = new ProxyBuilder()
.host("proxy.example.com")
.port(8080)
.user("proxy-user") // optional
.password("proxy-pass") // optional
.build();
await new InitializeBuilder()
.environment(USDataCenter.PRODUCTION())
.token(token)
.requestProxy(proxy)
.initialize();The SDK persists tokens via the TokenStore interface. The built-in FileStore saves tokens to a CSV file:
import { FileStore } from "@banana.inc/zoho-desk-nodejs-sdk";
const store = new FileStore("./zoho_desk_tokens.csv");To implement a custom store (e.g., database-backed), implement the TokenStore interface:
import type { TokenStore, Token } from "@banana.inc/zoho-desk-nodejs-sdk";
class DatabaseStore implements TokenStore {
async findToken(token: Token): Promise<Token | null> { /* ... */ }
async findTokenById(id: string): Promise<Token | null> { /* ... */ }
async saveToken(token: Token): Promise<void> { /* ... */ }
async deleteToken(id: string): Promise<void> { /* ... */ }
async getTokens(): Promise<Token[]> { /* ... */ }
async deleteTokens(): Promise<void> { /* ... */ }
}The SDK provides two structured error types:
Thrown when the Zoho Desk API returns a non-2xx response. Contains the HTTP status, Zoho error code, response body, and request URL.
import { ZohoApiError } from "@banana.inc/zoho-desk-nodejs-sdk";
try {
const tickets = await client.tickets.get();
} catch (err) {
if (err instanceof ZohoApiError) {
console.log(err.responseStatusCode); // e.g. 403
console.log(err.errorCode); // e.g. "INVALID_OAUTH"
console.log(err.message); // Human-readable message
console.log(err.requestUrl); // The URL that was called
console.log(err.responseBody); // Full parsed JSON body
}
}Thrown for SDK-level errors (invalid configuration, missing parameters, etc.).
import { SDKException } from "@banana.inc/zoho-desk-nodejs-sdk";
try {
await new InitializeBuilder().initialize();
} catch (err) {
if (err instanceof SDKException) {
console.log(err.code); // e.g. "MANDATORY_VALUE_ERROR"
console.log(err.message);
console.log(err.details); // Optional structured details
console.log(err.cause); // Optional root cause Error
}
}The SDK's 126 API modules are generated from Zoho's official OpenAPI specifications through a four-stage pipeline:
Pull OAS specs ──> Bundle per module ──> Kiota generate ──> Generate facade
- Pull — Clones the latest OpenAPI specs from
zoho/zohodesk-oas - Bundle — Creates self-contained spec bundles per API module with a manifest
- Generate — Runs Microsoft Kiota (via Docker) to produce TypeScript clients for each module
- Facade — Scans all generated clients and produces a unified
ZohoDeskClientwith lazy-loaded getters
To regenerate:
npm run generateNote: Files under
src/generated/andsrc/client/zoho-desk-client.tsare auto-generated. Do not edit them by hand.