Skip to content
Skip to main content

Sync contacts to Zoho CRM with Nylas

Last updated:

Zoho CRM runs across seven separate data centers, and a contact that exists in the US org at zohoapis.com is invisible to the EU org at zohoapis.eu. That regional split is the first thing that trips up a contacts sync. This recipe pulls a connected mailbox’s address book through the Nylas Contacts API, then upserts each person into the right Zoho data center using the v6 records API.

The Nylas side gives you one normalized contact schema across Google, Microsoft, and the other providers, so you write the Zoho mapping once. The Zoho side does the deduplication for you through its upsert endpoint, keyed on email. Wire a contact.updated webhook on top and the sync stays current without a nightly cron job.

How does the Nylas-to-Zoho contact sync work?

Section titled “How does the Nylas-to-Zoho contact sync work?”

The sync has two halves: read from Nylas, write to Zoho CRM. You list contacts with GET /v3/grants/{grant_id}/contacts, which returns one schema across 6 providers, map each record’s fields to Zoho’s Contacts module, then send a POST to Zoho’s /crm/v6/Contacts/upsert endpoint. Zoho matches on email and either creates or updates the record, so you never write duplicates.

The flow stays one-directional unless you add webhooks. For an initial backfill, paginate the full address book once. After that, subscribe to the contact.updated trigger so each change in the source mailbox pushes a single upsert instead of re-reading everything. Zoho’s upsert accepts up to 100 records per request, which sets your batch size.

StepSystemEndpoint
List contactsNylasGET /v3/grants/{grant_id}/contacts
Map fieldsYour codegiven_name → First_Name, etc.
Upsert recordsZoho CRMPOST /crm/v6/Contacts/upsert
Stay in syncNylascontact.updated webhook

This recipe stays Nylas-centric: the read path is fully worked, and the Zoho calls link out to Zoho’s v6 API reference rather than reproducing it. For a provider-agnostic version of the same pattern, see Sync Google Contacts into a CRM.

Start by reading the connected address book. The GET /v3/grants/{grant_id}/contacts request returns contacts in one unified schema across 6 providers (Google, Microsoft, iCloud, Yahoo, EWS, and IMAP), so the Zoho mapping you write works for every account. The default page size is 30 contacts and the maximum is 200 per request, set through the limit query parameter.

The response gives you given_name, surname, company_name, job_title, plus emails and phone_numbers arrays. You don’t need to learn each provider’s native contact format, since the schema is identical whether the grant is a Gmail or Outlook account. For filtering by email or inbox source before syncing, see List and sync contacts.

Map the contact fields to the Zoho CRM module

Section titled “Map the contact fields to the Zoho CRM module”

Zoho’s Contacts module names fields differently from the Nylas schema, and one field is non-negotiable. Last_Name is the only system-defined mandatory field in the Contacts module, so a contact with no surname needs a fallback value or Zoho rejects the record with a MANDATORY_NOT_FOUND error. Map the source fields onto Zoho’s API names like this:

Nylas fieldZoho Contacts API nameNotes
given_nameFirst_Nameoptional
surnameLast_Namerequired; fall back to email local-part if empty
emails[0].emailEmailupsert key
phone_numbers[0].numberPhoneoptional
company_nameAccount_Namelinks to an Account record
job_titleTitleoptional

The Email field is what Zoho uses to detect duplicates, so it doubles as your upsert key. Field API names come from Zoho’s Fields Metadata API if your org added custom fields; the 6 fields above are standard on every Zoho CRM account.

Send mapped contacts to Zoho’s upsert endpoint so existing people get updated instead of duplicated. The POST {api-domain}/crm/v6/Contacts/upsert request takes a data array of up to 100 records per request and a duplicate_check_fields array that tells Zoho which field identifies a match. Set it to ["Email"] to key on email address, matching the upsert key from your field mapping.

Each request body carries 6 fields per contact, and each response item returns an action of insert or update so you can log how many records were new versus matched. The upsert call counts against your daily API credit limit, which starts at 25,000 credits for Free and Standard editions per Zoho’s API limits documentation. Batch in groups of 100 to stay efficient.

Route the sync to the correct Zoho data center

Section titled “Route the sync to the correct Zoho data center”

A Zoho access token only works against the data center where the user’s org lives, and there are 7 of them. The OAuth token response returns 5 fields, one of which is api_domain, telling you exactly which base URL to call, so never hardcode zohoapis.com. Read api_domain from the token exchange and use it for every records request.

RegionAccounts OAuth domainAPI base URL
UShttps://accounts.zoho.comhttps://www.zohoapis.com
EUhttps://accounts.zoho.euhttps://www.zohoapis.eu
Indiahttps://accounts.zoho.inhttps://www.zohoapis.in
Australiahttps://accounts.zoho.com.auhttps://www.zohoapis.com.au
Japanhttps://accounts.zoho.jphttps://www.zohoapis.jp
Chinahttps://accounts.zoho.com.cnhttps://www.zohoapis.com.cn
Canadahttps://accounts.zohocloud.cahttps://www.zohoapis.ca

You exchange your authorization code for tokens at {accounts_domain}/oauth/v2/token, requesting the ZohoCRM.modules.contacts.ALL scope for read and write access to the Contacts module. The full domain list and redirect rules are in Zoho’s multi-DC documentation.

After the initial backfill, a contact.updated webhook keeps Zoho current without re-reading the whole address book. Nylas emits 2 contact triggers: contact.updated and contact.deleted. There’s no contact.created trigger, so a brand-new contact arrives as a contact.updated notification. Subscribe to both, and each change in the source mailbox delivers one payload you turn into a single Zoho upsert or delete.

The payload names the grant_id and contact id that changed, so your handler fetches that one contact and upserts it. Respond with a 200 status within 10 seconds or Nylas retries the delivery. For signature verification and the full event lifecycle, see Real-time webhooks.