← all tools
Recruiting CRM / ATS

Recruiterflow

Source into Recruiterflow, and move people through the pipeline.

Cost per callBYOK · vendor billed
Your own keyrequired
CategoryRecruiting CRM / ATS

Recruiterflow gets the full treatment: candidates in, attached to jobs, moved between stages, notes added. Your agent reads and writes the client and contact side too, so business development lands in the same system as delivery.

Recruiterflow is a destination and a starting point. The open roles already in it can kick off a sourcing run.

things you can ask for

  • Add these candidates and attach them to the role
  • Take this brief url, source with Prospeo, and file the longlist against the job
  • Move these three to interview and log the Granola notes
  • What is in the pipeline for this job?

works well with Recruiterflow

  • Prospeo

    the sourcing that fills the pipeline

  • Granola

    notes filed against the right candidate

  • Lemlist

    multichannel outreach off a Recruiterflow shortlist

  • Enrichley

    validate before anything leaves the system

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 Recruiterflow

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

HOW in lib/recruiterflow.py.

Auth & config

  • Base URL: https://recruiterflow.com/api/external · Auth: header RF-Api-Key (env RECRUITERFLOW_API_KEY; never hardcode). get_info() = safe read-only pilot.
  • ALL paths CONFIRMED from the official Swagger spec (api.recruiterflow.com, 2026-06-01 — pasted by user). Full endpoint index: reference/docs/recruiterflow/raw/endpoints.md. RF terminology: client = company, plus candidate/contact/job/deal/placement.
  • Paths & params (exact): account info is get_info()POST /info (there is no /account); candidate read is get_candidate()GET /candidate/get; get_job's param is job_id (not id); job statuses is GET /job-status/list (hyphenated); there is no resume-parse method (/resume/parse is not in the spec).

Operations (36 methods; long tail via request())

Candidates: list/get/search/add/update_candidate, add_candidate_to_job, move_candidate_to_stage, disqualify_candidate, add_candidate_note. Jobs: list/get/search/create/update_job, close_job, open_job, get_job_pipeline, list_job_statuses. Clients(companies): list/get/add/update/search_clients. Contacts: list/get/add/update/search_contacts, add_contact_note. Campaigns: list_campaigns, add_to_campaign, remove_from_campaign. Also get_info, list_users, get_user, create_placement. Everything else (activities, calls, emails, tasks, custom-fields, files, deals, placements, reference lists, locations, tags) → request(); all listed in the cached index.

Guardrail

Writes go to a production ATS — pilot one record, show payload+response, confirm before bulk.

Handoff

ATS write target: sourced+enriched candidates → add_candidate (with job_id) → review in Recruiterflow.

Callable surface — lib/recruiterflow.py

Import: from lib.recruiterflow import Recruiterflow → instantiate Recruiterflow() (reads key from env). Base: https://api.recruiterflow.com/api/external. Generic passthrough: request(method, path, *, params, json).

  • add_candidate(payload: dict) -> Any — POST /candidate/add — create a candidate (custom_fields as [{id,value}]).
  • add_candidate_note(payload: dict) -> Any — POST /candidate/notes/add — body {id, created_by, value, mentions[]}.
  • add_candidate_to_job(payload: dict) -> Any — POST /candidate/add-to-job — body {id, job_id, applied, added_time}; lands under Sourced.
  • add_client(payload: dict) -> Any — POST /client/add — create a client company.
  • add_contact(payload: dict) -> Any — POST /contact/add.
  • add_contact_note(payload: dict) -> Any — POST /contact/notes/add.
  • add_to_campaign(payload: dict) -> Any — POST /campaign/add-to-campaign — {campaign_id, id:[...], user_id}. Drives outreach — gate.
  • close_job(payload: dict) -> Any — POST /job/close — {job_id, user_id, close_reasons[]}.
  • create_job(payload: dict) -> Any — POST /job/create — {about_position, client_company_id, created_by, department_id, employment_type_id, locations[], title, ...}.
  • create_placement(payload: dict) -> Any — POST /placement-record/create — {user_id, placements:[{job,prospect,...}]}. See cached index for engagement-type variants.
  • disqualify_candidate(payload: dict) -> Any — POST /candidate/disqualify — body {id, job_id, reason, user_id}.
  • get_candidate(candidate_id: str | int) -> Any — GET /candidate/get?id= — read one candidate by id.
  • get_client(client_id: int) -> Any — GET /client/get?id=.
  • get_contact(contact_id: str | int) -> Any — GET /contact/get?id=.
  • get_info() -> Any — POST /info — external-API display-name info (safe read-only pilot). (There is no /account.)
  • get_job(job_id: str | int, *, include_stages: int | None = None) -> Any — GET /job?job_id= (param is job_id, not id). Optional include_stages=1.
  • get_job_pipeline(job_id: str | int) -> Any — GET /job/pipeline?job_id= — stage summary + detail.
  • get_user(*, id: int | None = None, email: str | None = None) -> Any — GET /user/get?id= or ?email=.
  • list_campaigns(**params) -> Any — GET /campaign/list.
  • list_candidates(**params) -> Any — GET /candidate/list (items_per_page, current_page, include_files, include_notes, include_count).
  • list_clients(**params) -> Any — GET /client/list — client companies.
  • list_contacts(**params) -> Any — GET /contact/list (include_files, include_notes, include_count).
  • list_job_statuses(**params) -> Any — GET /job-status/list (hyphenated).
  • list_jobs(**params) -> Any — GET /job/list (items_per_page, current_page, include_count, include_notes, include_description, only_open).
  • list_users(**params) -> Any — GET /user/list.
  • move_candidate_to_stage(payload: dict) -> Any — POST /candidate/move-to-stage — body {id, job_id, stage:{id,name}, user_id}.
  • open_job(payload: dict) -> Any — POST /job/open — {job_id, user_id}.
  • remove_from_campaign(payload: dict) -> Any — POST /campaign/remove-from-campaign — {campaign_id, id:[...]}.
  • search_candidates(payload: dict) -> Any — POST /candidate/search — filter body {conjunction, filters[], items_per_page, current_page, include_count}.
  • search_clients(payload: dict) -> Any — POST /client/search.
  • search_contacts(payload: dict) -> Any — POST /contact/search.
  • search_jobs(payload: dict) -> Any — POST /job/search — filter body (keys: job_client_company, job_department, job_id, name, custom_field.N).
  • update_candidate(payload: dict) -> Any — POST /candidate/update — full update (include id; provided keys replace existing data).
  • update_client(payload: dict) -> Any — POST /client/update — include id.
  • update_contact(payload: dict) -> Any — POST /contact/update — include id.
  • update_job(payload: dict) -> Any — POST /job/update — include job_id.