Prerequisites
Confirm the following before connecting:- Admin access in DualEntry to create integrations and map accounts.
- A Zoho Books user who can authorize OAuth for your organization and the Books organization you want to sync.
- A DualEntry company selected when you create the integration. The integration is bound to one company, but invoice lines can still post to other DualEntry companies when Zoho invoice custom fields match - see Configure invoice custom fields.
- Your chart of accounts configured in DualEntry, including the default income and default expense accounts you’ll map for auto-created items and line items not tied to a mapped Zoho item.
- Zoho Books invoice custom fields set up with the exact labels DualEntry reads:
Location,Service Line, andCompany. Without the right labels, location and service-line dimensions don’t populate. - A classification in DualEntry named
Service Typewhose lines match your ZohoService Linevalues by name. Location values resolve against a classification namedLocation(DualEntry creates or updates this classification automatically from Zoho values).
How to connect
Authentication is OAuth 2.0 (authorization code). DualEntry stores your Zoho access token, refresh token, accounts-server URL, and API domain, then refreshes the access token automatically when it expires.- In DualEntry, navigate to Settings → Integrations → Zoho Books.
- Choose Connect, complete Zoho’s consent flow in the popup, and record the accounts server Zoho returns during OAuth (for example
https://accounts.zoho.comor a regional variant). Zoho includes this value as theaccounts-serverparameter on the redirect. - Provide the authorization code, the accounts server, and the DualEntry company you want to bind the integration to. DualEntry exchanges the code for tokens, loads the Zoho Books organization list, sets the integration name from the first organization returned, marks the integration Connected, and queues an initial sync.
- Map the default Expense account and Income account integration rows to your DualEntry GL accounts. The pre-setup phase creates these placeholder rows; until both are mapped, the integration isn’t fully set up.
- Wait for the first sync to complete. Setup completion depends on a successful initial sync (the integration tracks last-successful-sync internally).
What syncs
Each sync run pulls Zoho Books data in this order: invoices → payments → customers → items → locations. Each phase uses its own budget for Zoho “detail” API calls so a single sync can’t exhaust your daily quota.
Customer, invoice, and customer-payment lists use Zoho’s
last_modified_time ordering and stop paging when the oldest row on a page is older than the integration’s creation date. Items and locations are fetched in full (paginated) when their sync steps run.
Very large backfills may need multiple sync runs across multiple days because of Zoho’s daily API quota. The integration enforces a per-sync cap on detail fetches so one run cannot exhaust the quota; if the cap is hit, the sync ends with a rate-limit error and the next scheduled run picks up where it left off.
Configure invoice custom fields
DualEntry reads Zoho Books invoice custom fields by label. Three labels are recognized today:Company- value must match a DualEntry company name. When it matches, the invoice posts to that company; otherwise the integration’s default company is used.Location- value must match a classification line name under your DualEntry classification namedLocation. The classification name and the Zoho field label both have to align.Service Line- when present on an invoice, line construction follows the service-line path. Service-line values are matched to classification lines under a DualEntry classification namedService Type.
Map payment-mode accounts
Each customer payment needs a DualEntry GL account for the Zoho payment mode (Stripe, Check, Cash, Bank Remittance, Bank Transfer, Credit Card, ACH/WIRE). DualEntry creates a placeholder integration record for each (mode, company) pair the first time it sees a payment in that mode, and you map it to the correct cash or undeposited-funds account. To complete payment-mode mapping:- Open the integration’s mapping screen after the first sync of customer payments.
- For each payment-mode row, choose the DualEntry GL account where payments in that mode should post.
- Resync to apply the mapping to any payments that were captured pending the mapping.
Troubleshoot sync errors
When a record fails to sync, the most common causes:
For Zoho-wide outages, see the Zoho status page.
FAQ
Does DualEntry write back to Zoho Books?
No. The connector only pulls - Zoho remains the system of record for AR operations.Which Zoho product is supported?
Zoho Books (API paths under/books/v3/). Zoho Invoice and other Zoho suites aren’t supported unless they expose the same Books endpoints your deployment targets.
Why does setup require income and expense accounts?
Newitem records created from Zoho lines fall back to the default income and expense accounts when Zoho doesn’t supply a mapped catalog item. Without those defaults, line creation fails.
Can I use a different classification name than “Service Type”?
No. The integration matchesService Line custom field values to lines under a DualEntry classification literally named Service Type. Renaming the classification breaks the mapping.
For maintainers
The details below describe deployment and connector configuration, not user-facing features.- OAuth app config (server-side environment):
ZOHO_CLIENT_ID,ZOHO_CLIENT_SECRET,ZOHO_REDIRECT_URI. The redirect URL registered in the Zoho API console must match the deployment’s redirect URI. - Internal create endpoint:
POST /api/integrations/zoho/acceptsauthorization_code,accounts_server(URL-encoded when required by your client),company_id, and an optionalintegration_idfor updates. - Internal trigger-sync endpoint:
POST /api/integrations/zoho/{integration_id}/trigger-sync/(blocked when the integration is paused). - Internal field plumbing: the integration sets
remote_idandnamefrom the first Zoho organization returned, queuessync_integrationon connect, and trackslast_sync_atfor setup-completion flags. - Incremental sync details: customer/invoice/payment lists page using
last_modified_timeand stop when the oldest row is older than the integration’screated_at, with a five-day delta window on invoice and payment upserts to allow for late-arriving Zoho updates. - API quota: Zoho publishes a 10,000 requests per day limit for typical Books API usage. The Zoho SDK respects
429responses withRetry-After-style backoff and retries a few times before surfacing the failure.
