Fob
Browse the docs

Use the library

The npm package that ships the CLI also exports the client the CLI is built on. Use it in workers, scheduled jobs and any Node.js code that needs Zoho Books.

npm install @finopsbricks/fob-zb
import { fobZb } from '@finopsbricks/fob-zb';

const zb = fobZb({
  client_id: process.env.FOB_ZB_CLIENT_ID,
  client_secret: process.env.FOB_ZB_CLIENT_SECRET,
  refresh_token: process.env.FOB_ZB_REFRESH_TOKEN,
  organization_id: process.env.FOB_ZB_ORGANIZATION_ID,
  region: process.env.FOB_ZB_REGION, // optional, default 'com'
});

const { data: overdue, page_context } = await zb.invoices.list({ status: 'overdue' });
const everyOpenBill = await zb.bills.getAll({ status: 'open' });
const invoice = await zb.invoices.get(overdue[0].invoice_id);

The package is an ES module and needs Node.js 18 or later.

Credentials#

The library never reads environment variables or the CLI's config file. You pass credentials in, which keeps a worker's credentials explicit.

FieldRequiredNotes
client_id, client_secretYesFrom your Self Client (how)
refresh_tokenYesThe quickest way to get one is with the CLI: add a profile, then copy refresh_token from ~/.fob/fob-zb/config.yml
organization_idYesFrom fob-zb organizations list, or zb.organizations.list()
regionNocom (default), eu, in, com.au, jp, ca, com.cn or sa. See Regions
persistTokenNoA callback that receives { access_token, access_token_expires_at, api_domain } after each refresh, if you want to cache the access token

Access tokens last an hour. The client refreshes them from the refresh token when needed, and retries a request once if Zoho reports the token as invalid.

Several organizations means several clients: fobZb(credsA) and fobZb(credsB). They can share one refresh token (why).

Resources#

Each resource is a property on the client, named in camelCase: invoices, bills, contacts, bankTransactions, chartOfAccounts, vendorPayments and so on, covering the same 27 resources as the CLI.

MethodReturns
list(params)One page: { data, page_context }. params are Zoho's query parameters, for example { status: 'overdue', page: 2 }
getAll(params)Every row, following pagination (up to 15,000 rows)
get(id)One record, or null
create(body), update(id, body), delete(id)On resources with writes. body uses Zoho's field names
Actions such as invoices.markSent(id), invoices.email(id, body), bankTransactions.exclude(id)On resources that have them

Request and response fields are Zoho's own. See the Zoho Books API reference. Every function has JSDoc types, so your editor autocompletes them.

Errors#

ErrorWhen
OAuthErrorToken exchange or refresh failed. code is Zoho's error, such as invalid_grant. The message links to troubleshooting
ApiErrorZoho rejected the request. code is Zoho's application error code and status is the HTTP status

Rate limits (HTTP 429) are retried automatically, up to three times, honouring Retry-After.

import { fobZb, ApiError, OAuthError } from '@finopsbricks/fob-zb';

try {
  await zb.invoices.markSent(id);
} catch (err) {
  if (err instanceof OAuthError) alertOps('Zoho connection needs re-authorising', err);
  else if (err instanceof ApiError) log.warn({ code: err.code }, err.message);
  else throw err;
}

Lower-level helpers#

For endpoints fob-zb doesn't wrap yet, the package also exports createTransport, plus the OAuth helpers exchangeGrantCode, refreshAccessToken, revokeRefreshToken, regionOf and apiConsoleUrl.