Zoho Connection

Build workflows that read, query, and (with confirmation) write Zoho CRM, People, and Recruit data — with regional OAuth, least-privilege scopes, and metadata-validated queries handled for you.

What you can build

Each row is a working pattern: the questions it answers, the tool your workflow calls, and a ready-made template. Every operation is bounded to the fields and forms your connection's refreshed metadata actually exposes — you never write raw Zoho queries or paths.

Where to find the templates: Every template named below ships in the dashboard gallery under Workflows → New → Templates → Zoho CRM & HR — open one, bind your connection, and run it. The same definitions live as JSON files in the Chatterfly repository under docs/dsl/06-examples/ for CLI-driven work (chatterfly validate / chatterfly deploy).
I want to…Use
Capture a CRM lead conversationally“A prospect from Acme wants a demo — log them.” An Agent collects details, crm_search_leads checks duplicates, and crm_create_lead writes after a Boolean confirmation. Template: Zoho CRM Lead Capture
Answer pipeline questions with numbers“What’s our open pipeline by stage?” “How much is Priya carrying this quarter?” Typed crm_query aggregations (sum, group by stage/owner) over Deals, Accounts, Contacts, and activities. Template: Zoho CRM Sales Review
Turn a fuzzy name into a stable record ID“Pull up Michael Johnson’s record” — even when three Johnsons exist. crm_resolve_entity, hr_resolve_employee, hr_resolve_candidate, hr_resolve_job_opening — exact matches resolve, close matches ask. Template: Zoho People Employee Resolution
Query HR records by field or date window“Who joined before June?” “Which employees are in the Support department?” hr_query_people with one equality filter or a between/lte date window on visible fields. Template: Zoho People Joining Date Review
Watch deadlines on a schedule“Every weekday, tell me whose work permit expires in the next 30 days.” A scheduled trigger plus an hr_query_people date window; the run reports who crosses the threshold. Template: Zoho People Work Permit Sweep
List and inspect recruiting pipeline“Show me our current candidates.” “What openings are still unfilled?” hr_query_recruit over Candidates and Job Openings, plus per-record reads. Template: Zoho Recruit Candidate Review
Preview a hire-to-onboarding handoff“Shanthini accepted — prep her onboarding.” Resolve a Recruit candidate, read their record, and show HR a review — reads only today. Template: Zoho Recruit → People Preview
Create/update employees with review“Add our new hire to Zoho People.” people_create_employee / people_update_employee; the validator requires a Boolean confirmation gate before either write. Template: Zoho People Onboarding
Note: Not available yet (fail-closed until verified provider contracts exist): People attendance/cases/checklists/compensation reads, Recruit writes and interviews, employee self-service writes, and HR history indexing.

Quick start

1

Connect Zoho

Create a connection in the dashboard (Connections → New → Zoho), pick only the capabilities your workflow needs, and complete OAuth in the browser. Then confirm it works:

bash
chatterfly connections list
chatterfly connections test <connection-id>
2

Discover what you can call

Operations and their exact JSON-schema parameters come from your connection — browse them instead of guessing names. Refresh metadata so queries validate against your org's real forms and fields:

bash
chatterfly connections operations <connection-id> --product people
chatterfly connections refresh-metadata <connection-id>
chatterfly connections metadata <connection-id>
3

Deploy a working example

Easiest path: open a template from Workflows → New → Templates → Zoho CRM & HR and deploy it from the dashboard. From the CLI, the same definitions live in the repository's docs/dsl/06-examples/ folder. Workflow JSON references a logical connection slug (for example zoho-people); you map it to your real connection at deploy time with --bind:

bash
chatterfly validate docs/dsl/06-examples/zoho-people-employee-resolution.json
chatterfly deploy docs/dsl/06-examples/zoho-people-employee-resolution.json \
  --bind zoho-people=<connection-id>
4

Run it

Trigger a test run from the CLI and answer its prompts in the dashboard, or open the workflow's page and start a run there:

bash
chatterfly trigger --deployment-id <deployment-id>
chatterfly runs get <run-id>

Setup

  1. Create a Zoho server-based OAuth client and register $APP_URL/api/auth/connect/zoho/callback plus $APP_URL/api/participant/auth/connect/zoho/callback.
  2. Configure a Zoho connection OAuth app in Workspace settings. Each workspace owns and rotates its resource client independently.
  3. Create a Zoho connection, select CRM, People, or Recruit capabilities, and choose static, caller, or participant fulfillment.
  4. Authorize through the generated OAuth action. Do not paste access or refresh tokens into workflow JSON.
Note: Zoho routes grants to regional accounts and API domains. Chatterfly accepts only exact supported US, EU, IN, AU, JP, CA, SA, and UK domain pairs and stores routing as protected credential metadata.

Capabilities

Capabilities are the unit of consent: each one maps to exact OAuth scopes and materializes a fixed set of operations on the connection. Pick the smallest set that covers your workflow — you can add more later (Zoho will ask you to reauthorize for the added scopes). Use the operation browser, chatterfly connections operations <id>, or MCP list_connection_operations instead of guessing names.

text
CRM: identity, lead read/write, field metadata
      People: employee/form read/write, HR form metadata, leave read, typed HR query
      Recruit: job-opening and candidate read, typed HR query
      Files: replay-safe protected employee-document upload
Note: Compensation and HR case capabilities are isolated in the connection form because they expose sensitive data. Select them only for private, least-privilege workflows; routine HR reads and reminder workflows do not need them.

Identity and regions

Every call runs as a verified Zoho identity — you choose which one when you create the connection:

FulfillmentWho the provider sees
staticA shared workspace account (e.g. an HR service account). Simplest; right for scheduled sweeps and team-level reads.
callerThe authenticated user who started the run from your backend. Right for user-attributed writes like lead capture.
participantA workflow participant who authorizes their own grant during the run, pinned to one role.

At OAuth time the callback probes CRM CurrentUser and pins the Zoho user, organization, granted scopes, region, accounts server, and API domain. At runtime the same authority is re-verified; missing scopes or untrusted routing fail the call instead of silently switching accounts.

Sales intelligence

Ask questions like "open pipeline by stage for this owner" with a typed, validated query instead of raw COQL. Select the CRM read capabilities needed for Deals and activities, plus crm_metadata. Add crm_record_history only to a static workspace connection that needs field-transition evidence.

json
{
  "version": 1,
  "module": "Deals",
  "metrics": [{ "function": "sum", "field": "Amount", "as": "pipeline_value" }],
  "group_by": ["Stage"],
  "filter": {
    "and": [
      { "field": "Owner", "op": "eq", "value": { "entity_id": "123456789" } },
      { "field": "Stage", "op": "not_in", "value": ["Closed Won", "Closed Lost"] }
    ]
  },
  "timezone": "America/New_York"
}

Pass this typed IR to crm_query; use crm_customer_query for Accounts or Contacts and crm_activity_query for Events, Tasks, and Calls. Chatterfly validates module fields, operators, limits, and timezone against connection metadata before privately compiling the provider request. Raw COQL, raw search criteria, and arbitrary Zoho paths are not accepted.

  1. Resolve approximate user, contact, or account names with crm_resolve_entity. When candidates are ambiguous, show the bounded candidates and require a selection from their returned stable IDs.
  2. Use the confirmed entity_id in the typed query. This filters records visible to the same connected authority; it never changes the OAuth actor or widens provider permissions.
  3. Configure allowlisted history modules and fields on the connection. Query transitions with crm_query_transitions using bounded RFC3339 dates, an IANA timezone, and at most 200 evidence rows.
  4. Report indexed_through, lag_seconds, warnings, and truncation with every history-derived claim. Timeline evidence describes past field changes, not current ownership or current record state.
  5. Group pipeline totals by currency and exclude null amounts. Calculate weighted pipeline only when trusted metadata exposes a numeric Probability field: sum(Amount * Probability / 100). Report included/excluded rows and truncation; never present it as calibrated prediction.
bash
chatterfly connections operations <connection-id> --product crm --query query
chatterfly connections metadata <connection-id>
chatterfly connections history-status <connection-id>
chatterfly connections history-rebuild <connection-id> --module Deals --days 30 --confirm
Note: Territory transition filtering is intentionally unsupported. Do not infer territory membership from history. For current-state constraints, use only fields and semantics present in the connection's verified metadata and operation schema.

In a non-production Zoho organization, seed similar owner names, mixed stages, activities, and known stage changes. Validate docs/dsl/06-examples/zoho-crm-sales-review.json, bind sales-crm to the sandbox connection, confirm ambiguous owner selection, compare returned record IDs and transition timestamps with Zoho, and verify stale or partial indexing is disclosed rather than presented as complete.

CRM lead workflow

The canonical write pattern: collect conversationally, check duplicates, show a review, and only write after an explicit yes. The validator enforces this shape for protected operations, so you cannot accidentally ship an unconfirmed write.

text
Agent(mode=converse) -> deterministic validation
-> crm_search_leads -> participant-visible review
-> Boolean confirmation -> Conditional
-> crm_create_lead
  1. Use a Zoho CRM sandbox or Developer Edition organization. Create a connection with crm_identity, crm_leads_read, crm_leads_write, and crm_metadata. These request user read, lead read/create/update, and field-metadata scopes only.
  2. Authorize the salesperson for caller fulfillment, then verify the connection reports the expected actor, organization, region, granted scopes, and no scope gaps.
  3. Refresh metadata and inspect crm_create_lead. Use the returned field API names, required fields, picklists, and layout contract; never substitute display labels for API names.
  4. Deploy the validated docs/dsl/06-examples/zoho-crm-lead-capture.json workflow and bind its logical sales-crm requirement to the sandbox connection.
  5. Run duplicate search, participant-visible review, and Boolean confirmation before crm_create_lead. CRM multi-status failures return typed, redacted provider errors.
bash
chatterfly connections catalog zoho
chatterfly connections operations <connection-id> --product crm --query lead
chatterfly connections metadata <connection-id>
chatterfly connections refresh-metadata <connection-id>
chatterfly deploy docs/dsl/06-examples/zoho-crm-lead-capture.json \
  --bind sales-crm=<sandbox-connection-id>
Verify attribution: In the sandbox, compare the created record ID from the redacted connection audit with Zoho CRM's record details and audit trail.Created By or the corresponding provider actor must be the salesperson who authorized the private run, not a browser-selected user or another concurrent account.

People onboarding

Create employee records from a reviewed dashboard form, optionally attaching documents — using a shared HR service account so every write is attributed consistently.

  1. Use a Zoho People sandbox or non-production organization. Create a static workspace connection with people_employee_read and people_employee_write; add people_files only when the workflow uploads employee documents.
  2. Authorize the shared HR service account. Refresh metadata to discover form links, field API names, required fields, types, and option values before mapping employee data.
  3. Deploy the validated docs/dsl/06-examples/zoho-people-onboarding.json workflow and bind hr-people to the sandbox connection.
  4. The dashboard form validates employee ID, names, and email, searches for existing employees, displays a review, and writes only after HR confirms.
  5. For documents, pass a protected run file reference to people_upload_employee_document. The runtime streams from object storage and reopens the source for a replay-safe retry instead of buffering bytes.
bash
chatterfly connections operations <connection-id> --product people --query employee
chatterfly connections metadata <connection-id>
chatterfly deploy docs/dsl/06-examples/zoho-people-onboarding.json \
  --bind hr-people=<sandbox-connection-id>
Verify the shared account: Confirm the employee and optional document in Zoho People, then match the provider record ID, operation, pack version, outcome, and duration in the Chatterfly connection audit. The provider actor should remain the configured workspace HR account across runs.

HR reads and resolution

The HR read surface follows one rhythm: refresh metadata, then query or resolve against what it exposes. Metadata is your org's contract — form link names, visible field API names, and cached entity directories — so queries validate before they ever reach Zoho.

bash
chatterfly connections refresh-metadata <connection-id>
chatterfly connections operations <connection-id> --product people --query hr
chatterfly connections operations <connection-id> --product recruit
ToolWhat it does
hr_query_peopleMetadata-validated People form query: selected visible fields, one equality filter or a between/lte ISO date window on a visible date field, bounded pagination, required IANA timezone.
hr_query_recruitBounded Candidates or JobOpenings list query over visible fields. No filter dialect yet — page through and post-process in the workflow.
hr_resolve_employeeResolve an approximate employee name to a stable People record ID from the cached, complete employee directory. Exact matches resolve; close matches return candidates to confirm.
hr_resolve_candidate / hr_resolve_job_openingThe same resolution flow over fresh Recruit metadata. Always confirm a returned stable ID for any non-exact match.

A typical resolution flow — used by zoho-people-employee-resolution.json — collects a name with an Input, calls the resolver, and branches on resolution: resolved uses the stable ID directly, confirm shows the named candidate for a yes/no, and no_match reports honestly.

Keep page sizes at or below 200. Do not author raw People search criteria, arbitrary provider paths, or a form link based on its display label — Chatterfly maps trusted field API names to provider labels for you, and unsupported query shapes fail closed with a reason instead of guessing.

Note: People department resolution and self-service leave writes are not published yet. Never substitute raw provider paths, criteria strings, or arbitrary Tool definitions for these bounded operations.

Private embeds

User-attributed writes require a private run. Your backend authenticates the salesperson, starts the run with an opaque broker grant reference or an explicit transferred authority, and sends only the participant token to the browser. Participant OAuth can also be initiated by the widget for the connection's pinned role. The browser never selects a salesperson or Zoho grant.

  1. Keep the deployment private and mint a stable cfw_ workflow key for the host backend.
  2. Resolve the authenticated salesperson to a canonical grant, exclusive transfer, or opaque broker reference. Pin the verified provider account, organization, actor, region, and scopes.
  3. Start POST /api/v1/runs with an Idempotency-Key, then mint the participant token for the workflow's fixed role.
  4. Give the browser only runId, participantToken, and apiBase. Test that changing actor/grant fields in browser input has no effect and cross-user authority is rejected.

See the widget guide for private token minting and persistence.

Troubleshooting

  • invalid_region: the callback returned a domain outside the supported exact regional pairs.
  • insufficient_scope: reconnect with the capability required by the operation.
  • identity_verification_failed: CRM CurrentUser could not verify the connected actor.
  • awaiting_authorization: reconnect the canonical authority, then retry the suspended run.
  • history_lag: transition evidence may omit recent changes; inspect history status and retry after indexing catches up.
  • Missing transition results: verify the static connection selected crm_record_history, the module and field are enabled, and the requested period is within retention.