Read Zoho Recruit (recruiting ATS/CRM) via its API v2 — search/get Candidates (dedup + criteria search before paying for external people data), list/search Clients (the agency's customer companies — an outbound exclusion list), and read Job Openings (the JD a search is built from). Use when the user mentions Zoho Recruit, checking the ATS before sourcing externally, or turning a job opening into search filters. Keys: self-serve OAuth connect — pick your data center and click Connect.
Ask for the outcome and your agent composes the run itself. Or start from a skill, a whole pipeline it already knows end to end.
browse all skills →Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.
Read the Zoho Recruit docs →HOW in lib/zoho_recruit.py.
api-console.zoho.com, paste its Client ID and Client Secret, pick your data center (us, eu, in, au, jp, ca, sa or cn) and click Connect. No refresh token is ever pasted — Hyreflow stores the connection and refreshes it automatically.get_fields to read an account's real field names (they vary per account).Candidates, Clients, Job_Openings, Contacts.Generic (any module): list_records, get_record, search_records, get_fields. Candidates: search_candidates, get_candidate. Clients: list_clients, search_clients. Job Openings: list_job_openings, search_job_openings, get_job_opening.
criteria, email, phone or word. criteria looks like ((Field:operator:value)and(Field:operator:value)) (≤10 conditions); escape a comma or paren inside a value with a backslash.per_page caps at 200; page on the response's paging info.get_fields(module=...) rather than guessing.Read source, not a write target: dedup + search Candidates before sourcing externally, build the outbound exclusion list from Clients, and turn a Job Opening's description into people_search filters.
lib/zoho_recruit.pyImport: from lib.zoho_recruit import ZohoRecruit → instantiate ZohoRecruit() (reads key from env).
get_candidate(candidate_id: str) -> Any — GET /Candidates/{id}.get_fields(module: str) -> Any — GET /settings/fields?module= — the account's field API names for a module (custom fields included). Needs scope ZohoRecruit.settings.fields.READ.get_job_opening(job_opening_id: str) -> Any — GET /Job_Openings/{id} — the full record, including the job description field.get_record(module: str, record_id: str) -> Any — GET /{module}/{id} — one full record.list_clients(*, page: int | None = None, per_page: int | None = None, fields: str | None = None) -> Any — GET /Clients — the agency's client companies (Client_Name, Website, …), one page ≤200.list_job_openings(*, page: int | None = None, per_page: int | None = None, fields: str | None = None) -> Any — GET /Job_Openings — one page of job openings (Job_Opening_Name, Job_Opening_Status, Client_Name…).list_records(module: str, *, page: int | None = None, per_page: int | None = None, fields: str | None = None, sort_by: str | None = None, sort_order: str | None = None) -> Any — GET /{module} — one page of records (per_page ≤200). fields = comma-separated API names.search_candidates(*, criteria: str | None = None, email: str | None = None, phone: str | None = None, word: str | None = None, page: int | None = None, per_page: int | None = None) -> Any — GET /Candidates/search — e.g. criteria ((Current_Job_Title:contains:engineer)and(City:equals:Austin)), or email=[email protected] for an exact dedup check, or word= for a keyword across the record.search_clients(*, criteria: str | None = None, word: str | None = None, page: int | None = None, per_page: int | None = None) -> Any — GET /Clients/search — e.g. criteria (Website:contains:acme.com) or (Client_Name:equals:Acme).search_job_openings(*, criteria: str | None = None, word: str | None = None, page: int | None = None, per_page: int | None = None) -> Any — GET /Job_Openings/search — e.g. criteria (Job_Opening_Status:equals:In-progress).search_records(module: str, *, criteria: str | None = None, email: str | None = None, phone: str | None = None, word: str | None = None, page: int | None = None, per_page: int | None = None) -> Any — GET /{module}/search — exactly one of criteria | email | phone | word. No match → {"data": []}.