xMatix ships three Zoho connectors — Zoho Books, Zoho Payroll and Zoho People — that read Zoho data into xMatix through sync profiles, so you can migrate off Zoho or run xMatix alongside it with Zoho as the source. The shipped Zoho sync is inbound: it keeps xMatix records in step with what Zoho holds and does not push xMatix changes back. You set it up once per product — usually from a starter template — and the sync profile then runs on its own schedule.
Overview
Each Zoho product is a separate external system with its own connector type (zoho.books, zoho.payroll, zoho.people), its own connection and its own sync profile. All three sign in with a Zoho OAuth refresh token. What differs is how each product reports change, and that decides how often you can afford to sync it:
| Product | What you sync | How changes are found |
|---|---|---|
| Zoho Books | Masters (currencies, taxes, chart of accounts, customers and vendors, items, bank accounts), documents (sales orders, invoices, credit notes, purchase orders, bills, expenses, estimates), payments, bank transactions, and their lines | Last-modified ordering for most modules; a full read for the rest |
| Zoho Payroll | Salary components, pay runs, and employees, departments, designations, work locations and holidays | A full read every cycle |
| Zoho People | Forms such as employees, leave requests, attendance, departments, designations and holidays | A modified-since filter |
When to use it
- You are moving from Zoho to xMatix and want masters and history brought across, then kept current until you switch Zoho off.
- Zoho remains the book of record for part of the business (for example payroll or HR) and xMatix needs a current copy.
Use something else when you need to write into Zoho. The sync path is inbound only. A one-off outbound call can be hand-built as an integration flow, but xMatix as the receiving system is the supported setup.
How xMatix handles it
Sign-in. The connector exchanges the client id, client secret and refresh token for an access token at the Zoho accounts server of the data centre you name. It reuses the token for 50 minutes and refreshes it once if Zoho answers 401. For Books and Payroll it calls the regional API host that Zoho returns with the token, so an account that moved between data centres still routes correctly. People is always called on its own regional People host. Every Books and Payroll call carries the connection's organization id automatically.
Credential rejections. When Zoho rejects the credential itself (for example invalid_client or invalid_code), the connector stops asking Zoho for 10 minutes and fails runs with the same message in the meantime. A retry cannot fix a wrong credential, so the pause saves calls.
Rate limits. Zoho limits each organization to 100 requests a minute and a daily allowance of 1,000–10,000 calls, depending on the plan. A 429 answer is treated as a temporary failure that is retried later, not a dead run. For Books, the extra per-document detail calls that line declarations need are paced at no more than 90 a minute per connection.
Zoho Books change detection. Books is read 200 records a page.
- Most modules are read newest first, and the read stops as soon as it reaches records already synced. A quiet module costs one page a cycle. If a cycle hits its page limit before it reaches the synced records, it keeps no new position, and the next cycle reads the same changes again. This means a record is never skipped.
- The first read of a module, and any reseed, goes oldest first so the module builds up in order.
- Chart of accounts, bank accounts, bank transactions, expenses, contact persons and the settings collections (taxes, currencies) have no usable ordering. They are read whole every cycle, and the engine's content comparison decides what changed. This is correct but heavier on large organizations.
Zoho Payroll change detection. Payroll publishes no modification stamp on any module, so every cycle reads each module whole. Only rows whose content changed are written.
Zoho People change detection. People is form-based: the external object you declare is a form link name (for example P_EmployeeView), not a table. Each cycle asks for records added or modified since the last completed cycle, 200 at a time. A cycle that stops before it has read the whole feed keeps no new position, so the next cycle reads the same changes again. A form record is matched on recordId, then Zoho_ID, EmployeeID or Employee_ID, whichever the form returns.
Deletes. No Zoho product publishes deletions. Books voids posted documents rather than deleting them, and the void arrives as an ordinary status change. Keep delete mode None on Zoho entities. A record deleted in Zoho stays in xMatix.
Field discovery. Zoho has no describe endpoint. When you run Discover fields, the connector reads one sample record and lists its fields, including your organization's own cf_ custom fields. An empty module has nothing to sample, so it shows no fields. Fields ending in _formatted are locale renderings that change without the record changing, so discovery marks them as calculated and leaves them out.
Prerequisites
- The integration administration capability, which opens Setup → Platform → Integrations and Setup → Platform → Sync profiles.
- The Admin role on the integration app, to add its secrets (credentials and rotation). A starter template that creates the app gives you that role.
- A Zoho API client registered in the same data centre as your Zoho account, its client id and client secret, and a refresh token with read scopes for the modules you will sync.
- For Books and Payroll, the organization id of that product. Payroll has its own organization id. It is not the Books one, even for the same company. People needs none.
- A dedicated xMatix user to act as the sync identity. The sync reads and writes xMatix as that user, so give it access to every entity you sync.
Procedure
Step 1 — Start the profile from a template
Go to Setup → Platform → Sync profiles and choose Start from template in the page header. In Starter template, pick Zoho Books, Zoho Payroll or Zoho People. The dialog shows the template's description and its posture: for Books, a migration from Zoho that is inbound only, where Zoho's values win and deletes are ignored.
If xMatix has no profile for that product yet, a warning says one will be created in Draft. Fill in:
- Profile name — optional; leave it empty to use the template's suggested name.
- Integration app and Connection — leave both empty to let the template create them. The app is named "Zoho" and is reused if an active app of that name already exists. A Zoho connection of the same type already in that app is reused too.
- Sync identity user id — required: the id of the dedicated sync user.
If a profile already exists for the product, the template extends that profile instead. xMatix keeps one profile per external system. The option reads "(extends "…")", and the button changes from Create profile to Apply to profile.
Step 2 — Fill the parameters and pick the entities
Under Parameters, enter Zoho data centre (for example in, com, eu, com.au, jp) and, for Books or Payroll, the product's organisation id. In Books it is under Settings → Organization Profile.
Under Entities, tick what you want. The dialog pre-ticks the template's recommended entities that exist in your xMatix, and shows in profile for entities you already sync. An entity marked not in this tenant cannot be ticked. Zoho entities offer only the Inbound direction. A line shows "Requires … (added automatically)" when it depends on another entity: for example, Invoice lines bring in Invoices and Items.
Leave Run field discovery for entities the template marks ticked if the connection's secrets already exist. Otherwise untick it and run discovery after Step 3.
Choose Create profile (or Apply to profile). The result lists each entity as Created, Updated, Unchanged, Dependency (added because another entity needs it) or Skipped, with how many field maps were added, discovered and kept, followed by warnings and next steps. Nothing is compiled or activated yet.
Step 3 — Add the connection's secrets
The created connection refers to three secrets: zoho-client, zoho-secret and zoho-refresh. Open Setup → Platform → Integrations, open the "Zoho" app, and on Secrets add those three with your client id, client secret and refresh token. All three Zoho templates use the same three names in the same app. If Books, Payroll and People use different Zoho clients or refresh tokens, give each connection its own secret names in its configuration.
Step 4 — Test the connection
On the app's Connections tab, open the Zoho connection and choose Test Connection. A pass reads "Authenticated against" followed by the API host Zoho returned. The test performs only the token exchange. It proves the client, secret, refresh token and data centre, but not the organization id or the token's scopes, so follow it with a first cycle (Step 6) before you rely on it.
Step 5 — Discover fields and review the maps
Open the profile. Its header shows the status, external system, integration app, connection, schedule, default conflict policy, overlap window and the sync identity it runs as. On the Entities tab, check the entity list. For entities that need your organization's custom fields, run Discover fields and review the maps on the Mapping tab. People templates ship no maps at all, because forms differ per portal: every People entity's maps come from discovery.
Before you go on, check the Currency maps. The Books template maps the Zoho currency code into Label and the currency name into Name, which is the reverse of the usual xMatix convention, where Name holds the code. Change the maps if your currencies should follow the convention.
Step 6 — Compile, activate and run
Choose Compile in the profile header. Compile generates one flow per entity and direction. Set the profile Active and compile again. Watch the first cycles under Setup → Platform → Monitoring → Sync Monitor, or on the profile's own tabs, and open a few synced records to confirm the values.
Step 7 — Add more entities later
On the profile's Entities tab, choose Apply template, tick the additional entities and apply. Entities and maps you already have are kept exactly as you left them. Only missing entities and maps are added, and a link whose target entity has since been added is filled in. The organization id is needed only when the apply writes something that uses it. Compile again afterwards.
What each template contains
| Template | Entities (xMatix ← Zoho) | Schedule (UTC) |
|---|---|---|
| Zoho Books | Currency ← settings/currencies; Tax ← settings/taxes (filter tax_type=tax&filter_by=Taxes.All); Tax Group ← settings/taxes (filter tax_type=tax_group&filter_by=Taxes.All); GL Account ← chartofaccounts; Account ← contacts; Item ← items; Bank Account ← bankaccounts; Contact ← contactpersons; Bank Statement Line ← banktransactions; Order ← salesorders; Invoice ← invoices; Invoice Line ← invoices/line_items; Credit Note ← creditnotes; Purchase Order ← purchaseorders; Bill ← bills; Bill Line ← bills/line_items; Expense Voucher ← expenses; Payment ← customerpayments | Hourly, on the hour |
| Zoho Payroll | Salary Component ← salarycomponents; Payroll Run ← payruns | Daily at 02:00 |
| Zoho People | Holiday ← P_Holidays; Resource ← P_EmployeeView; Employee ← P_EmployeeView; Leave Request ← P_ApplyLeave (not pre-ticked) | Every 6 hours |
Every template sets the conflict policy to ExternalWins (Zoho's value wins) and a 300-second overlap window. Payments come across in base currency. Each entity with + discovery in the dialog adds your organization's own fields when discovery runs.
Zoho Books modules and collections
Beyond the template's entities, a Books profile can declare these external objects with Add entity on the profile:
| External object | What it reads |
|---|---|
vendorcredits, vendorpayments, estimates, projects, journals | The module's records |
invoices/line_items, bills/line_items, expenses/line_items, salesorders/line_items, purchaseorders/line_items, creditnotes/line_items, estimates/line_items, vendorcredits/line_items, journals/line_items | Document lines, read from each changed document's detail |
customerpayments/invoices, vendorpayments/bills | Which invoices a customer payment settles, and which bills a vendor payment settles |
creditnotes/invoices_credited, vendorcredits/bills_credited, vendorcredits/vendor_credit_refunds | Credit-note applications to invoices, vendor-credit applications to bills, and refunds a vendor paid back |
invoices/detail, bills/detail, creditnotes/detail, vendorcredits/detail, customerpayments/detail, vendorpayments/detail, estimates/detail | Header amounts that only the document detail carries (round-off, adjustment, bank charges, exchange rate, estimate totals), written onto records an earlier declaration already brought in |
invoices/charges, bills/charges, creditnotes/charges | Header adjustment, untaxed shipping and after-tax discount as separate charge rows |
banktransactions/journal, banktransactions/journal_lines | Bank transactions that have no Books document (transfers, deposits, expense refunds, other income) as journals |
Rules for these declarations:
- Lines need their parent. Every line, settlement or detail declaration costs one extra Zoho call per changed parent document. It must map a reference back to its parent, and the parent must run at a lower rank.
- Journals double-post. Discovery lists Journals with a DO NOT SYNC warning. xMatix posts its own ledger from the documents, so importing Zoho journals is right only for locked-period history.
- Some line collections are unconfirmed. The detail shapes for sales-order, purchase-order and credit-note lines, and for journal lines, follow Zoho's published API but have not been checked against a live organization. Check them against real data before you rely on them, because a mismatch returns no lines and no error.
External filters
A Zoho entity's external filter is a set of Zoho's own query parameters, written name=value&name=value, for example status=unpaid on invoices. Segments without a name or a value are ignored rather than failing the cycle. Books also ignores parameters it does not support, so a mistyped filter narrows nothing. Two filters you will use:
- Taxes and tax groups share one Books collection. Split them with
tax_type=taxandtax_type=tax_group. - Books hides inactive taxes by default. Add
filter_by=Taxes.Allto include them.
Example
An administrator at a distribution company, Riverbend Traders, is moving its accounting from Zoho Books to xMatix.
- Under Setup → Platform → Sync profiles, they choose Start from template, pick Zoho Books, leave the app and connection empty, and enter the sync user's id. They enter data centre
inand the Books organization id. They untick Bank Statement Line for now and untick discovery. - The result says the profile was created in Draft with its Integration app and connection. Seventeen entities show Created. Invoice Line and Bill Line show their map counts.
- In the "Zoho" app they add
zoho-client,zoho-secretandzoho-refresh. Test Connection answers "Authenticated against" the India API host. - They run Discover fields on Account and Item, which adds two custom fields. They compile, set the profile Active and compile again.
- The first hourly cycle brings in 1,240 customers and vendors and 3 years of invoices. The next cycle reads one page per sorted module and finds 12 changed invoices.
- A month later they choose Apply template on the Entities tab and tick Bank Statement Line. It shows Created and every other entity shows Unchanged.
Training
Practice exercise
In a sandbox with a Zoho Books trial organization:
- Start a Zoho Books profile from the template, tick only Currency, Tax and Tax Group, and leave discovery unticked. Expected: three entities Created, the profile in Draft, and a connection in the "Zoho" app.
- Add the three secrets and test the connection. Expected: "Authenticated against" with your regional host.
- Compile, activate, compile and wait for a cycle. Expected: your Books currencies and taxes appear in xMatix, with inactive taxes included because of
filter_by=Taxes.All. - Apply the template again and tick Item. Expected: Item Created; Currency, Tax and Tax Group Unchanged.
Quick reference
- One sync profile per Zoho product. Applying a template again extends that profile.
- Zoho sync is inbound only. Keep delete mode None.
- The Payroll organization id is not the Books one. People needs none.
- The OAuth client, refresh token and data centre must all match.
- Test Connection proves sign-in only. A first cycle proves the organization id and scopes.
- Books: sorted modules are cheap; chart of accounts, banking, expenses and settings are read whole.
- Payroll: read whole every cycle, so sync it nightly.
- People: declare form link names such as
P_EmployeeView. - Lines cost one Zoho call per changed parent document.
- Never sync Zoho journals into periods where xMatix also posts the documents.
Permissions
- Opening Sync profiles, starting from a template and compiling need the integration administration capability.
- Adding or rotating the connection's secrets needs the Admin role on the integration app. Creating, editing and testing connections needs the Connection Manager role or higher.
- The sync reads and writes xMatix as its Sync identity, so it can never sync more than that user can see or change.
- In Zoho, the refresh token's scopes decide what each read can reach. A module outside its scopes fails on the first cycle even though the connection test passed.
Configuration
- Setup → Platform → Integrations → (app) — the secrets and the connection. The connection's configuration holds
dataCenter(com,in,eu,com.au,jp,ca,com.cn,saorae, withauandcnaccepted as short forms; when it is left out,comis used), theclientId,clientSecretandrefreshTokensecret references,organizationIdfor Books and Payroll, and an optionalapiDomain. Books and Payroll useapiDomainonly when Zoho returns no host with the token. People always uses it when set. - Setup → Platform → Sync profiles → (profile) — entities, external objects, external filters, field maps, ranks, schedule, conflict policy and overlap window. Watch cycles under Setup → Platform → Monitoring → Sync Monitor (sync health).
- Nightly reconciliation (on the profile's Schedule tab) — available for Zoho Books. Each night it lists every record Books changed in the window and reports records missing from xMatix or soft-deleted here. Modules read whole each cycle and line declarations are skipped. Books has no delete feed, so "deleted in Zoho" is never reported. Zoho differences are reported for you to reseed the entity, never repaired automatically. The Zoho templates do not turn reconciliation on.
Scheduling guidance
| Product | Suggested cadence | Why |
|---|---|---|
| Books | Hourly (the template default) | Sorted reads are cheap, but modules read whole and the daily allowance punish tighter schedules |
| Payroll | Nightly | Every cycle reads every module whole |
| People | Every 15 minutes to 6 hours | The cost of a cycle follows the volume of change |
Common problems
"cannot authenticate: Zoho rejected the configured credential with OAuth error '…'"
Zoho refused the client id, client secret or refresh token. Check that the three secrets hold current values, and that the client and token were issued in the data centre the connection names: a client from another data centre always fails. After you fix it, runs can keep failing with the same message for up to 10 minutes.
"token exchange failed"
Zoho's accounts server returned an error that is not a credential rejection. The message includes Zoho's answer. Check the data centre value and try again later.
"is missing required string 'organizationId'"
A Books or Payroll connection has no organization id. Add organizationId to the connection's configuration. If Zoho instead answers "Organization not found", the id is wrong for that product, for example a Books id on a Payroll connection.
Runs fail with HTTP 429
Zoho's per-minute or daily allowance is used up. The run is retried later. If it keeps happening, sync less often, sync fewer entities, or move Payroll to a nightly schedule.
The Contact entity from the template fails with "Invalid URL"
Books does not serve contact persons at contactpersons. The readable path is contacts/contactpersons. Change the entity's external object to contacts/contactpersons, compile, and let the entity reseed.
Tax groups cannot be read
Books does not list settings/taxgroups. Declare Tax Group on settings/taxes with the external filter tax_type=tax_group, as the template does.
An entity's lines never arrive
Lines exist only in each document's detail, so check that the line entity references its parent and runs at a higher rank than the parent. For sales-order, purchase-order, credit-note and journal lines, the expected detail shape is unconfirmed, and a mismatch returns no lines without an error.
Discovery lists no fields
The module is empty, so there was no record to sample. Map the fields by hand, or run Discover fields again once the module has data. If the template apply warned "Field discovery for … could not run", add the connection's secrets and run Discover fields from the Mapping tab.
"… references '…', which is not in the profile"
A template map links to an entity you did not select, so the link is dropped for now. Apply the template again with that entity ticked, and the link is filled in.
An entity shows Skipped in the template result
The note "… does not exist in this tenant; nothing declared." means the target entity is not available in your xMatix. Nothing was added for it.
Common questions
Can xMatix write back to Zoho?
Not through sync. The Zoho templates offer only the Inbound direction, and the Zoho connectors declare no keyed upsert or bulk write. A specific outbound call to a Zoho API can be hand-built as an integration flow, but treat xMatix as the system that receives. Zoho stays the source until you switch it off, and the ExternalWins conflict policy keeps Zoho's value whenever both sides changed.
Which organization id goes in the connection?
The one that belongs to the product the connection is for. Books and Payroll each issue their own organization id for the same company. A Books id on a Payroll connection fails every request even though the sign-in works, and the connection test still passes, because it checks only the sign-in. People connections take no organization id.
Why does a Books module read everything every hour?
Some modules — chart of accounts, bank accounts, bank transactions, expenses, contact persons, taxes and currencies — cannot be sorted by last-modified time, so they are read whole every cycle and the engine keeps only the changes. That is correct, just heavier. On a large organization, banking and expenses are the modules to watch. Widen the schedule if they use up the daily allowance.
What happens when someone deletes a record in Zoho?
Nothing. No Zoho product publishes deletions, and Zoho entities use delete mode None, so the xMatix record stays. Voided Books documents do sync, as the status change they are. Nightly reconciliation for Books can show records that are missing in xMatix or soft-deleted here, but it cannot detect a deletion in Zoho.
