
# 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:

```bash
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:

```bash
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](https://www.zoho.com/books/api/v3/oauth/):

`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:

```text
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.
