Summary and recommendation
The Zoho Books REST API (base: https://www.zohoapis.com/books/v3) exposes full CRUD for users under the /users resource, authenticated via OAuth 2.0.
Four scopes gate user operations: ZohoBooks.settings.READ, .CREATE, .UPDATE, and .DELETE.
Every request requires an organization_id query parameter - omitting it returns an 'Organization not found' error regardless of token validity.
Base URLs are region-partitioned;
using zohoapis.com against an EU-hosted org returns auth errors.
Access tokens expire after one hour;
refresh token rotation is required for unattended automation.
Rate limits are enforced at 100 requests per minute per organization, with HTTP 429 on breach and no documented Retry-After header.
Pagination is offset-based via a page parameter (1-indexed, max 200 records per page) with no cursor support.
For identity graph use cases, the user object exposes user_id, email, role_id, status (invited or active), and segmentation flags (is_customer_segmented, is_vendor_segmented).
These fields are sufficient to map a Zoho Books user back to an IdP identity, but the API does not expose last-login timestamps or session data.
SCIM provisioning is handled entirely through Zoho Directory (https://directory.zoho.com/scim/v2/), not through the Books REST API;
the two credential sets and authorization flows are fully separate.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Zoho One or Enterprise plan with Zoho Directory; SCIM is not a native Zoho Books feature |
Authentication
Auth method: OAuth 2.0
Setup steps
- Register your application at https://api-console.zoho.com/ to obtain a Client ID and Client Secret.
- Direct the user to the Zoho OAuth 2.0 authorization URL: https://accounts.zoho.com/oauth/v2/auth with the required scopes and redirect_uri.
- Exchange the returned authorization code for an access token and refresh token via POST to https://accounts.zoho.com/oauth/v2/token.
- Include the access token in the Authorization header as 'Zoho-oauthtoken {access_token}' on all API requests.
- Use the refresh token to obtain new access tokens before expiry (access tokens expire in 1 hour).
- For data center compliance, use the region-specific base URL (e.g., .eu, .com.au, .in) matching the user's Zoho account region.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| ZohoBooks.settings.READ | Read access to organization settings, including user list. | GET /users, GET /users/{user_id} |
| ZohoBooks.settings.CREATE | Create/invite new users to the Zoho Books organization. | POST /users |
| ZohoBooks.settings.UPDATE | Update existing user details and roles. | PUT /users/{user_id} |
| ZohoBooks.settings.DELETE | Remove users from the Zoho Books organization. | DELETE /users/{user_id} |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| user_id | string | Unique identifier for the user within the Zoho Books organization. | system-generated | read-only | Returned in all user responses. |
| name | string | Full display name of the user. | required | optional | |
| string | Email address used to identify and invite the user. | required | read-only | Must be a valid Zoho account email or will trigger an invitation. | |
| role_id | string | Role assigned to the user within the organization (e.g., admin, staff). | required | optional | Retrieve valid role IDs from GET /roles. |
| status | string | Activation status of the user (e.g., active, invited). | system-generated | read-only | Reflects whether the user has accepted the invitation. |
| is_customer_segmented | boolean | Indicates if the user has restricted access to specific customers. | optional | optional | |
| is_vendor_segmented | boolean | Indicates if the user has restricted access to specific vendors. | optional | optional | |
| photo_url | string | URL of the user's profile photo. | system-generated | read-only | Managed via Zoho Accounts profile. |
Core endpoints
List Users
- Method: GET
- URL:
https://www.zohoapis.com/books/v3/users?organization_id={org_id} - Watch out for: organization_id query parameter is mandatory on every request.
Request example
GET /books/v3/users?organization_id=10234695
Authorization: Zoho-oauthtoken 1000.xxxxx
Response example
{
"code": 0,
"message": "success",
"users": [
{"user_id": "460000000016001", "name": "Jane Doe", "email": "jane@example.com", "role_id": "460000000016002", "status": "active"}
]
}
Get User
- Method: GET
- URL:
https://www.zohoapis.com/books/v3/users/{user_id}?organization_id={org_id}
Request example
GET /books/v3/users/460000000016001?organization_id=10234695
Authorization: Zoho-oauthtoken 1000.xxxxx
Response example
{
"code": 0,
"message": "success",
"user": {
"user_id": "460000000016001",
"name": "Jane Doe",
"email": "jane@example.com",
"role_id": "460000000016002",
"status": "active"
}
}
Invite User
- Method: POST
- URL:
https://www.zohoapis.com/books/v3/users?organization_id={org_id} - Watch out for: Creates an invitation; user status remains 'invited' until they accept. The email must correspond to an existing or new Zoho account.
Request example
POST /books/v3/users?organization_id=10234695
Content-Type: application/json
{"name":"John Smith","email":"john@example.com","role_id":"460000000016002"}
Response example
{
"code": 0,
"message": "The user has been invited.",
"user": {
"user_id": "460000000016099",
"name": "John Smith",
"email": "john@example.com",
"status": "invited"
}
}
Update User
- Method: PUT
- URL:
https://www.zohoapis.com/books/v3/users/{user_id}?organization_id={org_id} - Watch out for: Email cannot be updated via this endpoint.
Request example
PUT /books/v3/users/460000000016099?organization_id=10234695
Content-Type: application/json
{"name":"John A. Smith","role_id":"460000000016003"}
Response example
{
"code": 0,
"message": "User information has been saved.",
"user": {
"user_id": "460000000016099",
"name": "John A. Smith",
"role_id": "460000000016003"
}
}
Delete User
- Method: DELETE
- URL:
https://www.zohoapis.com/books/v3/users/{user_id}?organization_id={org_id} - Watch out for: Cannot delete the organization owner/admin. Deleting a user removes their access but does not delete their Zoho account.
Request example
DELETE /books/v3/users/460000000016099?organization_id=10234695
Authorization: Zoho-oauthtoken 1000.xxxxx
Response example
{
"code": 0,
"message": "The user has been removed."
}
Get Current User
- Method: GET
- URL:
https://www.zohoapis.com/books/v3/users/me?organization_id={org_id} - Watch out for: Returns details of the authenticated user associated with the OAuth token.
Request example
GET /books/v3/users/me?organization_id=10234695
Authorization: Zoho-oauthtoken 1000.xxxxx
Response example
{
"code": 0,
"message": "success",
"user": {
"user_id": "460000000016001",
"name": "Jane Doe",
"email": "jane@example.com",
"role_id": "460000000016002"
}
}
List Roles
- Method: GET
- URL:
https://www.zohoapis.com/books/v3/roles?organization_id={org_id} - Watch out for: role_id values from this endpoint are required when creating or updating users.
Request example
GET /books/v3/roles?organization_id=10234695
Authorization: Zoho-oauthtoken 1000.xxxxx
Response example
{
"code": 0,
"message": "success",
"roles": [
{"role_id": "460000000016002", "role_name": "Staff", "description": "Standard staff role"}
]
}
Rate limits, pagination, and events
- Rate limits: Zoho Books API enforces per-organization rate limits. The official documentation states a limit of 100 API calls per minute per organization.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: When the rate limit is exceeded, the API returns HTTP 429. The official docs do not explicitly document rate-limit response headers or a Retry-After header. Daily limits may also apply; consult the official API docs for current values.
- Pagination method: offset
- Default page size: 200
- Max page size: 200
- Pagination pointer: page
| Plan | Limit | Concurrent |
|---|---|---|
| All paid plans | 100 requests/minute per organization | 0 |
- Webhooks available: No
- Webhook notes: Zoho Books does not offer native webhook events specifically for user management actions (user invited, user removed, role changed). Zoho Books webhooks exist for transactional events (invoices, payments, etc.) but not for user/settings changes.
- Alternative event strategy: Poll GET /users periodically to detect changes, or use Zoho Flow/Zoho Directory event triggers for identity lifecycle events.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Zoho One or Enterprise plan with Zoho Directory; SCIM is not a native Zoho Books feature
Endpoint: https://directory.zoho.com/scim/v2/
Supported operations: Create User, Update User, Deactivate User, List Users, Get User
Limitations:
- SCIM provisioning targets Zoho Directory/Zoho One, not Zoho Books directly; access to Zoho Books is granted via Zoho Directory group/app assignment.
- Requires SSO to be configured as a prerequisite.
- Available only on Zoho One or equivalent enterprise-tier subscriptions.
- Supported IdPs include Okta and Microsoft Entra ID (Azure AD); Google Workspace and OneLogin are not officially listed.
- Deprovisioning removes the user from Zoho Directory; Zoho Books access is revoked via app assignment removal, not a direct Books API call.
Common scenarios
Three integration patterns cover the majority of programmatic user management needs.
Invite and role assignment: obtain ZohoBooks.settings.READ and .CREATE scopes, call GET /roles to resolve a valid role_id, then POST /users with name, email, and role_id.
The response reflects status: invited - the user must accept the email before status transitions to active.
Downstream provisioning logic must poll GET /users/{user_id} and gate on status before treating the user as fully provisioned.
Role update: requires .READ and .UPDATE scopes.
Resolve user_id via GET /users, confirm the target role_id via GET /roles, then PUT /users/{user_id} with the new role_id.
Email is immutable through this endpoint;
name and role are the only patchable fields.
SCIM-based deprovisioning via Zoho Directory: applicable only on Zoho One or equivalent enterprise tier with SSO configured.
Point Okta or Entra ID at https://directory.zoho.com/scim/v2/ with a Directory-issued SCIM bearer token.
IdP deactivation sends a SCIM PATCH active=false to Zoho Directory, which propagates access revocation to Zoho Books via app assignment - no Books REST API call is made.
Invite a new staff user and assign a role
- Authenticate via OAuth 2.0 and obtain an access token with ZohoBooks.settings.READ and ZohoBooks.settings.CREATE scopes.
- Call GET /books/v3/roles?organization_id={org_id} to retrieve available role_id values.
- Call POST /books/v3/users?organization_id={org_id} with body {name, email, role_id} to send the invitation.
- Poll GET /books/v3/users/{user_id}?organization_id={org_id} and check status field until it transitions from 'invited' to 'active'.
Watch out for: The user must accept the email invitation before their status becomes 'active'. Automated provisioning flows must account for this asynchronous step.
Update a user's role
- Authenticate with ZohoBooks.settings.READ and ZohoBooks.settings.UPDATE scopes.
- Call GET /books/v3/users?organization_id={org_id} to find the target user_id.
- Call GET /books/v3/roles?organization_id={org_id} to confirm the new role_id.
- Call PUT /books/v3/users/{user_id}?organization_id={org_id} with body {role_id: 'new_role_id'}.
Watch out for: Only role_id and name can be updated; email is immutable via this endpoint.
Deprovision a user via SCIM through Zoho Directory
- Ensure Zoho One/Enterprise plan is active and SSO is configured in Zoho Directory.
- Configure your IdP (Okta or Entra ID) with the Zoho Directory SCIM endpoint (https://directory.zoho.com/scim/v2/) and a SCIM bearer token from Zoho Directory settings.
- When the user is deactivated in the IdP, the IdP sends a SCIM PATCH/PUT to set active=false in Zoho Directory.
- Zoho Directory propagates the deactivation, revoking the user's access to Zoho Books (and other assigned Zoho apps).
Watch out for: This flow does not call the Zoho Books REST API directly. If you only use the Books REST API to delete a user, the user's Zoho Directory/SSO account remains active and they may regain access if re-assigned.
Why building this yourself is a trap
The primary integration trap is treating the Zoho Books REST API and Zoho Directory SCIM as interchangeable deprovisioning paths - they are not. A DELETE /users/{user_id} call removes the user from the Books organization but leaves their Zoho account and Directory profile intact.
If your IdP or a Zoho One admin re-assigns the Books app, the user regains access with no audit trail of the interim deletion. Full lifecycle control requires coordinating both systems: Books REST API for org-level role and access management, Zoho Directory SCIM for identity-layer deactivation. A second trap is the asynchronous invitation state.
Automated flows that POST to /users and immediately attempt downstream actions against the new user will encounter status: invited responses until the human accepts the email. There is no API mechanism to force-activate a user or bypass the invitation step.
For teams building against an identity graph, note that Zoho Books user objects carry no last-login or session metadata. Cross-referencing Books users against an authoritative identity store requires matching on email or user_id externally - the API does not provide the signals needed to detect dormant accounts natively.
An MCP server with 60+ deep IT/identity integrations can bridge this gap by correlating Books user records with IdP activity signals across the full identity graph.
Automate Zoho Books workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.