← all tools
Recruiting CRM

TrackerRMS

Read/write TrackerRMS (recruiting ATS/CRM) via its v1 API — search/get/create/update resources (candidates), opportunities (jobs), clients (companies) and contacts, plus shortlists, longlists and activities. Use when the user mentions TrackerRMS or Tracker, pushing candidates into the ATS, or reading jobs/companies. Keys: not a self-serve integration yet.

Cost per callfree
Your own keyrequired, contact us to enable
CategoryRecruiting CRM

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 →

What your agent reads before it touches TrackerRMS

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 TrackerRMS docs →
Show the raw playbookwritten for the agent

HOW in lib/trackerrms.py.

Auth & config (two-step token)

  • Base URL: https://evoglapi.tracker-rms.com (override via env if the account is on a different host).
  • Auth: TWO-STEP. An OAuth2 access token (minted externally via TrackerRMS's own /oAuth2/Authorize/oAuth2/Token dance) is exchanged for a short-lived API JWT via POST /api/Auth/ExchangeToken, and the JWT goes in Authorization: Bearer <jwt> on every call.
  • Env: TRACKERRMS_ACCESS_TOKEN (the OAuth2 access token), optionally TRACKERRMS_JWT (a current JWT, skips the exchange) and TRACKERRMS_BASE_URL. Never hardcode.
  • The JWT expires; a 401 mid-call triggers one automatic re-exchange + re-issue — no manual refresh loop needed.

Noun mapping

Resource = candidate, Opportunity = job, Client = company, Contact = person at a client, OpportunityResource = shortlist entry / placement.

Operations

Resources (candidates): search_resources, search_resources_paged, get_resource, create_resource, update_resource, create_resource_from_resume, get_resource_skills. Opportunities (jobs): search_opportunities, search_opportunities_paged, get_opportunity, create_opportunity, update_opportunity, publish_opportunity, get_opportunity_applicants, get_opportunity_longlist, add_to_longlist. Clients (companies): search_clients, get_client, create_client, update_client. Contacts (people at a client): search_contacts, get_contact, create_contact, update_contact. Shortlists/placements: shortlist_resource, search_opportunity_resources, get_opportunity_resource, update_opportunity_resource, get_shortlist. Activities/metadata/users: create_activity, search_activities, get_metadata, search_users.

  • Searches take a search DTO, not query paramssearch_resources/search_opportunities/search_clients/search_contacts/search_activities/search_users all POST a payload object.
  • maxResults/pageNumber are rejected, not clamped: they must be positive integers or the call errors before any request is sent.
  • publish_opportunity has no dedicated endpoint — Tracker advertises a job online by PATCHing the opportunity's advertDetails block (publishOnline: true, advertStatus: "A").
  • get_metadata is the free read that resolves the status/type/source/team ids a write needs first — call it before a create/update that references one of those ids.
  • Record ids are integers. No delete operation is exposed for any resource.

Guardrail

Production ATS writes — pilot one, confirm, then bulk. TrackerRMS documents no idempotency key, so never blind-retry a create — a timeout or unclassifiable response may already have landed. A 401 is different: it means Tracker REJECTED the request, so nothing landed upstream, which is why the adapter can safely re-exchange the JWT and re-issue the call once, for any verb.

Handoff

ATS write target: enriched candidates → create_resource.

Callable surface

Call via the CLI: hyreflow tools execute trackerrms <method> --payload '{...}' (preview with --dry-run; hyreflow tools get trackerrms <method> returns the live contract + cost). Any endpoint without a typed method is reachable through the tool's generic request passthrough.

  • add_to_longlist(payload: dict) -> Any — POST /api/v1/Opportunity/Longlist — add candidates to a job's longlist.
    • example: {"resourceId":5501,"opportunityId":4821}
  • batch(method: str, calls: list, *, stop_on_error: bool = False) -> dict — Batched get/search/update/create: run one method over many inputs, rate-limited. Each call is a value -> method(value), or {"args": [...], "kwargs": {...}} for multi-arg methods like update_resource(id, payload). Destructive methods (delete) are refused. See lib/_base.run_ops.
  • create_activity(payload: dict) -> Any — POST /api/v1/Activity — log an activity.
    • example: {"subject":"Intro call with Acme","note":"Discussed the Senior Platform Engineer role.","activityType":{"id":3,"name":"Call"},"contactDirection":"Outbound","dueDateTime":"2026-09-20T14:00:00Z"}
  • create_client(payload: dict) -> Any — POST /api/v1/Client — create a company.
    • example: {"name":"Acme Corp","website":"acme.example.com","contactDetails":{"telephone":"+1-555-0101"},"status":{"id":1,"name":"Active"},"sectors":[{"id":12,"name":"Technology"}]}
  • create_clients_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — POST /api/v1/Client for many records, paced by the 600 rpm limiter.
  • create_contact(payload: dict) -> Any — POST /api/v1/Contact — create a client contact.
    • example: {"firstName":"Grace","surname":"Hopper","jobTitle":"VP Engineering","client":{"id":4821,"name":"Acme Corp"},"contactDetails":{"email":"[email protected]"},"isPrimary":true}
  • create_contacts_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — POST /api/v1/Contact for many records, paced by the 600 rpm limiter.
  • create_opportunities_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — POST /api/v1/Opportunity for many records, paced by the 600 rpm limiter.
  • create_opportunity(payload: dict) -> Any — POST /api/v1/Opportunity — create a job.
    • example: {"name":"Senior Platform Engineer","description":"Own the deployment platform for Acme's core product.","client":{"id":4821,"name":"Acme Corp"},"status":{"id":2,"name":"Live"},"numberOfPositions":1,"advertDetails":{"publishOnline":true,"advertStatus":"A","title":"Senior Platform Engineer","location":"Remote (US)"}}
  • create_resource(payload: dict) -> Any — POST /api/v1/Resource — create a candidate.
    • example: {"firstName":"Ada","surname":"Lovelace","jobTitle":"Platform Engineer","contactDetails":{"email":"[email protected]","mobilePhone":"+1-555-0100"},"status":{"id":1,"name":"Registered"},"availableForWork":true,"sectors":[{"id":12,"name":"Technology"}]}
  • create_resource_from_resume(payload: dict) -> Any — POST /api/v1/Resource/CreateFromResume — create a candidate by parsing an uploaded resume.
    • example: {"fileName":"ada-lovelace-resume.pdf","fileData":"<base64-encoded file contents>","resourceSource":"Career site"}
  • create_resources_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — POST /api/v1/Resource for many records, paced by the 600 rpm limiter.
  • get_client(client_id: Any) -> Any — GET /api/v1/Client/{Id} — a single company by id.
  • get_contact(contact_id: Any) -> Any — GET /api/v1/Contact/{Id} — a single client contact by id.
  • get_metadata() -> Any — GET /api/v1/MetaData — statuses, types, sources and teams for the calling user (the ids a write needs).
  • get_opportunity(opportunity_id: Any) -> Any — GET /api/v1/Opportunity/{Id} — a single job by id.
  • get_opportunity_applicants(opportunity_id: Any) -> Any — GET /api/v1/Opportunity/{Id}/Applicants — candidates who applied to a job.
  • get_opportunity_longlist(opportunity_id: Any) -> Any — GET /api/v1/Opportunity/{Id}/Longlist — a job's longlist.
  • get_opportunity_resource(opportunity_resource_id: Any) -> Any — GET /api/v1/OpportunityResource/{Id} — a single shortlist entry by id.
  • get_resource(resource_id: Any) -> Any — GET /api/v1/Resource/{Id} — a single candidate by id.
  • get_resource_skills(resource_id: Any) -> Any — GET /api/v1/Resource/{resourceId}/Skills — the skills recorded against a candidate.
  • get_shortlist(opportunity_id: Any) -> Any — GET /api/v1/OpportunityResource/Shortlist/{opportunityId} — the shortlist for a job.
  • publish_opportunity(opportunity_id: Any, advert: dict | None = None) -> Any — PATCH /api/v1/Opportunity/{Id} — advertise a job online.
  • search_activities(payload: dict) -> Any — POST /api/v1/Activity/Search — search activities by a search DTO.
    • example: {"resourceId":5501,"mustHaveResource":true,"maxResults":25}
  • search_clients(payload: dict) -> Any — POST /api/v1/Client/Search — search companies by a search DTO.
    • example: {"searchTerm":"Acme","maxResults":25}
  • search_contacts(payload: dict) -> Any — POST /api/v1/Contact/Search — search client contacts by a search DTO.
    • example: {"searchTerm":"Hopper","clientId":4821,"maxResults":25}
  • search_opportunities(payload: dict) -> Any — POST /api/v1/Opportunity/Search — search jobs by a search DTO.
    • example: {"searchTerm":"Platform Engineer","clientId":4821,"maxResults":25}
  • search_opportunities_paged(payload: dict) -> Any — POST /api/v1/Opportunity/PagedSearch — paged search of jobs.
    • example: {"searchTerm":"Platform Engineer","clientId":4821,"maxResults":25}
  • search_opportunity_resources(payload: dict) -> Any — POST /api/v1/OpportunityResource/Search — search shortlist entries / placements.
    • example: {"opportunityId":4821,"state":"Shortlisted","maxResults":25}
  • search_resources(payload: dict) -> Any — POST /api/v1/Resource/Search — search candidates by a search DTO.
    • example: {"searchTerm":"Lovelace","maxResults":25,"onlyMyRecords":false}
  • search_resources_paged(payload: dict) -> Any — POST /api/v1/Resource/PagedSearch — paged search of candidates.
    • example: {"searchTerm":"Lovelace","maxResults":25,"onlyMyRecords":false}
  • search_users(payload: dict) -> Any — POST /api/v1/User/Search — search users by a search DTO.
    • example: {"searchTerm":"Lovelace","maxResults":25}
  • shortlist_resource(payload: dict) -> Any — POST /api/v1/OpportunityResource — add a candidate to a job's shortlist.
    • example: {"resourceId":5501,"opportunityIds":[4821],"statusId":6,"note":"Strong technical fit."}
  • update_client(client_id: Any, payload: dict) -> Any — PATCH /api/v1/Client/{Id} — partial update of a company.
  • update_contact(contact_id: Any, payload: dict) -> Any — PATCH /api/v1/Contact/{Id} — partial update of a client contact.
  • update_opportunity(opportunity_id: Any, payload: dict) -> Any — PATCH /api/v1/Opportunity/{Id} — partial update of a job.
  • update_opportunity_resource(opportunity_resource_id: Any, payload: dict) -> Any — PATCH /api/v1/OpportunityResource/{Id} — partial update of a shortlist entry.
  • update_resource(resource_id: Any, payload: dict) -> Any — PATCH /api/v1/Resource/{Id} — partial update of a candidate.