Organizations and tokens
One login, many organizations#
A Zoho refresh token belongs to your Zoho user, not to one organization. If your login can see several Zoho Books organizations (say your company, a client and a test org), one Self Client and one refresh token reach all of them. fob-zb chooses the organization per request with organization_id.
So for a second organization you don't need a new Self Client or a new grant code. Copy an existing profile's credentials and point the copy at the other organization:
fob-zb organizations list # IDs of every org your login can see
fob-zb config profiles add client-co --from myorg --organization-id 60012xxxxx
fob-zb config profiles list
Then switch between them:
fob-zb config profiles use client-co # change the default
fob-zb invoices list --profile myorg # or pick one for a single command
Revoking access#
Profiles copied with --from share one refresh token, so revoking it disconnects all of them.
| Action | Effect |
|---|---|
fob-zb auth logout | Revokes the profile's refresh token at Zoho and clears it locally. Every profile that shares that token stops working |
fob-zb config profiles remove name | Deletes the profile locally only. The token keeps working for the other profiles |
| Delete the Self Client in the API Console | Revokes every token issued to it, for every profile and worker using it |
To stop using one organization, use config profiles remove. Use auth logout only when you mean to cut off every profile on that token.
Choosing scopes#
ZohoBooks.fullaccess.all lets fob-zb read and write everything it supports. Zoho also offers narrower scopes in the form ZohoBooks.module.OPERATION, where the operation is READ, CREATE, UPDATE, DELETE or ALL. The modules are listed in Zoho's OAuth documentation:
contacts, settings, estimates, invoices, customerpayments, creditnotes, projects, expenses, salesorders, purchaseorders, bills, debitnotes, vendorpayments, banking, accountants
Read-only scopes#
For an AI agent that should answer questions but never change your books, generate the grant code with read scopes only. Enter them comma-separated in the Scope field:
ZohoBooks.contacts.READ,ZohoBooks.settings.READ,ZohoBooks.estimates.READ,ZohoBooks.invoices.READ,ZohoBooks.customerpayments.READ,ZohoBooks.creditnotes.READ,ZohoBooks.projects.READ,ZohoBooks.expenses.READ,ZohoBooks.salesorders.READ,ZohoBooks.purchaseorders.READ,ZohoBooks.bills.READ,ZohoBooks.debitnotes.READ,ZohoBooks.vendorpayments.READ,ZohoBooks.banking.READ,ZohoBooks.accountants.READ
Write commands then fail with an authorization error from Zoho. Put this in its own profile, such as myorg-readonly, and keep it separate from a full-access profile.
Where credentials are stored#
| Used by | Location | Contains |
|---|---|---|
| CLI | ~/.fob/fob-zb/config.yml, file mode 0600. Override the folder with FOB_ZB_CONFIG_DIR | Client ID, client secret, refresh token, cached access token, organization ID and name, region |
| Workers and CI | FOB_ZB_CLIENT_ID, FOB_ZB_CLIENT_SECRET, FOB_ZB_REFRESH_TOKEN, FOB_ZB_ORGANIZATION_ID, optional FOB_ZB_REGION | The same, without the cache |
When more than one source is available, fob-zb uses the first match: --profile flag, then a complete set of FOB_ZB_* variables, then the current profile. Treat the client secret and refresh token like passwords.