Loxo is where the work gets recorded. Your agent creates candidate records, reads your live jobs, and links people to the roles they are up for. These are real writes to a production system, so it does one and shows you before it does the rest.
Loxo is the destination. Everything the pipeline produced ends up filed here.
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 Loxo docs →HOW in lib/loxo.py.
https://app.loxo.co/api/{LOXO_AGENCY_SLUG} — Loxo is agency-scoped. The host is fixed; an agency subdomain 404s.LOXO_API_KEY (Bearer), LOXO_AGENCY_SLUG (bare slug, e.g. acme — a pasted URL/domain is reduced to its first label). Never hardcode.page/per_page; people and companies paginate via scroll_id (+ a Lucene query). Index: reference/docs/loxo/raw/endpoints.md.Jobs: list_jobs, get_job, create_job, update_job. People: list_people, get_person, create_person, update_person. Companies (accounts): list_companies, iter_companies, get_company, create_company, update_company, plus list_dynamic_fields + company_custom_fields for the agency's custom fields. Job↔candidate: list_job_candidates (GET /jobs/{id}/candidates), apply_to_job (POST /jobs/{id}/apply).
apply_to_job (POST /jobs/{job_id}/apply) — multipart/form-data with REQUIRED email+name+phone (+ optional resume file). create_job_candidate is a deprecated alias.list_companies paginates by cursor only: pass the scroll_id each response returns, with a per_page up to 100 — there is no page param. iter_companies(max_records=…) walks the whole account list.custom_hierarchy_1..6 {id, value} pairs, and each slot holds a different field per agency. Read the definitions once with list_dynamic_fields, then pass a {"custom_hierarchy_3": "Therapeutic area"} map to company_custom_fields(company, labels).ATS write target: enriched candidates → create_person / apply_to_job (assign to a job).
Company-first BD: iter_companies → each account's url (website) → hyreflow_native career-page scraping for their open roles → contacts via the people_search waterfall.
lib/loxo.pyImport: from lib.loxo import Loxo → instantiate Loxo() (reads key from env). Generic passthrough: request(method, path, *, params, json).
apply_to_job(job_id: str, *, email: str, name: str, phone: str, resume: tuple | None = None, **fields) -> Any — POST /jobs/{job_id}/apply — add a candidate to a job (CONFIRMED).company_custom_fields(company: dict, labels: dict | None = None) -> dict — Local (no HTTP): a company's custom_hierarchy_1..6 slots as {field_name: value}.create_company(payload: dict) -> Any — POST /companies — create a company (form-encoded, company[...] params).create_job(payload: dict) -> Any — POST /jobs — create a job.create_job_candidate(job_id: str, payload: dict) -> Any — DEPRECATED alias → apply_to_job (POST /jobs/{id}/apply, multipart).create_person(payload: dict) -> Any — POST /people — create a person. (Resume upload needs multipart via request().)get_company(company_id: str) -> Any — GET /companies/{id} — one company, with its website (url) and custom fields.get_job(job_id: str) -> Any — GET /jobs/{id}.get_person(person_id: str) -> Anyiter_companies(*, per_page: int = 100, max_records: int = 1000, **params) -> Iterator[dict] — GET /companies — follow the scroll cursor until the account list is exhausted.list_companies(**params) -> Any — GET /companies — search/list companies (accounts, employers).list_dynamic_fields() -> Any — GET /dynamic_fields — the agency's custom (dynamic) field definitions.list_job_candidates(job_id: str, **params) -> Any — GET /jobs/{job_id}/candidates — candidates on a job, with pipeline stage (CONFIRMED).list_jobs(**params) -> Any — GET /jobs — paginated (page, per_page).list_people(**params) -> Any — GET /people — paginated people/candidates.update_company(company_id: str, payload: dict) -> Any — PUT /companies/{id} (form-encoded, company[...] params).update_job(job_id: str, payload: dict) -> Any — PUT /jobs/{id}.update_person(person_id: str, payload: dict) -> Any