Write into Bullhorn, at the scale Bullhorn shops work at.
Bullhorn is a recruiting CRM, and one of the 52 tools hyreflow connects to Claude, ChatGPT and other AI assistants. Through hyreflow, an AI agent can run 13 actions: 3 that look something up and 10 that create or change records. Bullhorn runs on your own Bullhorn account: hyreflow charges no credits for it, and Bullhorn bills you as before.
Your agent searches and updates Bullhorn candidates, contacts, companies and job orders, piloting one record before it writes the rest. Bullhorn is the enterprise end of recruitment CRM, and writes there are real and hard to undo.
Bullhorn is the destination. Everything upstream converges here.
With hyreflow connected, you ask for the outcome in plain language and the assistant runs Bullhorn for you. For example:
In a hyreflow run, Bullhorn is most often paired with Apollo, ZoomInfo, SmartLead and Aircall.
Through hyreflow, an AI agent can run 13 Bullhorn actions: 3 that look something up and 10 that create or change records. Bullhorn runs on your own Bullhorn account: hyreflow charges no credits for it, and Bullhorn bills you as before.
| Bullhorn action | What it does | Cost through hyreflow |
|---|---|---|
| Search | Looks something up | no hyreflow credits |
| Create candidate | Creates or changes a record | no hyreflow credits |
| Create candidates batch | Creates or changes a record | no hyreflow credits |
| Create client contact | Creates or changes a record | no hyreflow credits |
| Create client contacts batch | Creates or changes a record | no hyreflow credits |
| Create client corporation | Creates or changes a record | no hyreflow credits |
| Create client corporations batch | Creates or changes a record | no hyreflow credits |
| Create entity | Creates or changes a record | no hyreflow credits |
| Create job order | Creates or changes a record | no hyreflow credits |
| Create job orders batch | Creates or changes a record | no hyreflow credits |
| Bullhorn action | What it does | Cost through hyreflow |
|---|---|---|
| Query | Looks something up | no hyreflow credits |
| Update entity | Creates or changes a record | no hyreflow credits |
| Get entity | Looks something up | no hyreflow credits |
One credit is priced on the pricing page. The full price list for every tool is in the pricing tables.
6 hyreflow skills call Bullhorn as part of the run. A skill is a whole pipeline your agent already knows, end to end.
use whena role comes in and you want to know who you already have before you pay to source anyone new
use whena strong applicant comes in for the wrong job, and your team is working dozens of other roles they might fit
use whenyou have an excellent candidate on the bench and no live role to put them into
use whena hiring manager who knows you has changed employer, and your CRM still shows them at the company they left
use whenyour database is full of people you have not spoken to in years, and you do not know which of them have been promoted or changed employer since
use whenyou have a finished shortlist or prospect list as a file, and it needs to be in your ATS or CRM without doubling up people you already hold
hyreflow ships a written playbook for Bullhorn, 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 Bullhorn docs →HOW in lib/bullhorn.py. Docs: bullhorn.github.io/rest-api-docs.
_login)GET rest.bullhornstaffing.com/rest-services/loginInfo?username=… → oauthUrl + restUrl for the user's datacenter (e.g. auth-emea / rest-emea). Tenants are cluster-pinned: the global auth host answers a cross-cluster login with its login page, not a code.GET {oauthUrl}/authorize (client_id + username/password + action=Login) → 302 with ?code=.POST {oauthUrl}/token (grant_type=authorization_code) → access_token.POST {restUrl}/login?version=*&access_token=… → BhRestToken + restUrl.
Then every call hits {restUrl}<path> with BhRestToken. The client re-logs in once on 401.BULLHORN_CLIENT_ID, BULLHORN_CLIENT_SECRET, BULLHORN_USERNAME, BULLHORN_PASSWORD; optional BULLHORN_REDIRECT_URI — leave unset so Bullhorn uses the OAuth app's registered URI (a mismatch fails authorize with "Invalid Redirect URI"). Never hardcode. BYOK: api_key = client ID; client_secret, username, password are the extra fields.ping() = safe read-only pilot.search(entity, query, fields) — GET /search/{Entity} Lucene (indexed: Candidate, JobOrder, ClientContact, ClientCorporation…). query(entity, where, fields) — GET /query/{Entity} SQL-style (non-indexed). get_entity (GET /entity/{Entity}/{id}), create_entity (PUT /entity/{Entity}), update_entity (POST /entity/{Entity}/{id}). request() covers the rest. BhRestToken via query param is valid (also accepted as a header). All path patterns verified.
Production ATS writes — pilot one record, confirm, then bulk.
ATS write target: enriched candidates → create_entity("Candidate", …).
lib/bullhorn.pyImport: from lib.bullhorn import Bullhorn → instantiate Bullhorn() (reads key from env). Generic passthrough: request(method, path, *, params, json).
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_entity(entity, id, payload). Destructive methods (delete) are refused. See lib/_base.run_ops.create_candidate(payload: dict) -> Any — PUT /entity/Candidate — create a candidate.create_candidates_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — Create many candidates sequentially while respecting the 1,500 req/min limit.create_client_contact(payload: dict) -> Any — PUT /entity/ClientContact — create a contact.create_client_contacts_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — Create many client contacts sequentially, rate-limited.create_client_corporation(payload: dict) -> Any — PUT /entity/ClientCorporation — create a company.create_client_corporations_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — Create many companies sequentially, rate-limited.create_entity(entity: str, payload: dict) -> Any — PUT /entity/{entity} — create a record.create_job_order(payload: dict) -> Any — PUT /entity/JobOrder — create a job.create_job_orders_batch(payloads: list[dict], *, stop_on_error: bool = False) -> dict — Create many job orders sequentially, rate-limited.get_entity(entity: str, entity_id: int, *, fields: str = '*') -> Any — GET /entity/{entity}/{id}?fields=...ping() -> Any — GET ping — verify session (safe read-only pilot).query(entity: str, where: str, *, fields: str = '*', **params) -> Any — GET /query/{entity}?where=...&fields=... — SQL-style where (non-indexed entities).search(entity: str, query: str, *, fields: str = '*', **params) -> Any — GET /search/{entity}?query=...&fields=... — Lucene-style search (indexed entities).update_entity(entity: str, entity_id: int, payload: dict) -> Any — POST /entity/{entity}/{id} — update a record.Yes. hyreflow is a connector for Claude, ChatGPT and other AI assistants. Once hyreflow is connected, you ask for the outcome in plain language and the assistant calls Bullhorn for you. hyreflow exposes 13 Bullhorn actions this way.
Yes. Bullhorn runs on your own Bullhorn account. You connect it to hyreflow once, hyreflow charges no credits for the calls, and Bullhorn bills you as before.
Bullhorn runs on your own Bullhorn account: hyreflow charges no credits for it, and Bullhorn bills you as before.
Add the hyreflow MCP server to your assistant and sign in. hyreflow calls the Bullhorn API directly and exposes its actions as tools the assistant can run, so there is no separate Bullhorn server to install.
Connect hyreflow to Claude or ChatGPT and ask for the outcome. It reads the playbook above before it makes a single call.