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.
| Field | Required | Notes |
|---|---|---|
client_id, client_secret | Yes | From your Self Client (how) |
refresh_token | Yes | The quickest way to get one is with the CLI: add a profile, then copy refresh_token from ~/.fob/fob-zb/config.yml |
organization_id | Yes | From fob-zb organizations list, or zb.organizations.list() |
region | No | com (default), eu, in, com.au, jp, ca, com.cn or sa. See Regions |
persistToken | No | A 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.
| Method | Returns |
|---|---|
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#
| Error | When |
|---|---|
OAuthError | Token exchange or refresh failed. code is Zoho's error, such as invalid_grant. The message links to troubleshooting |
ApiError | Zoho 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.