JD → Shortlist
Paste a job description, get a qualified shortlist with reachable contacts.
The most-requested recruiter play. Paste a job description → extract searchable filters → source candidates (general search + prioritized competitor profiles) → enrich the work history → qualify against the JD → enrich the candidate channel → ready for outreach.
The competitor leg prioritizes, it never filters exclusively. You always run the general search; competitor-sourced candidates get boosted to the top because they carry the domain logic. General profiles fill the rest of the shortlist.
Capability chain
JD → PARSE (AI) → [DISCOVER COMPETITORS (exa)] → SEARCH (general + competitor-priority, merged & ranked)
→ ENRICH PROFILE (linkedin_profile — work history with dates) → QUALIFY (AI)
→ ENRICH CONTACT (personal email + LinkedIn) → (outreach)Enrich the profile before you score it. Search output is a snapshot — current title, employer, location — which cannot separate a long-tenured specialist from a recent career changer, and hides relevant experience sitting behind an unrelated current title. Contact enrichment stays after qualification, so you only pay for a survivor's email.
Inputs
JD text (any language). Optional: target count N (default 10), location override,
"competitors only" (off by default).
Steps
Parse the JD → a canonical PeopleQuery
Extract titles[] (role + searchable EN/DE variants), skills[] (must-haves only),
certifications[], min_experience_years, locations[], seniority, keywords[],
plus the hiring company {name, domain, category} and reports_to.
Location rule: remote with no geo/language requirement → locations: []; otherwise
honor the stated region and any language requirement ("German C2" → DACH).
Discover competitors (add-on)
exa.answer("direct competitors of {company} ({domain}) in {category}") → parse
Name (domain) pairs. Skip if the JD has no clear company/category.
Don't use exa.find_similar on the homepage — it returns directories, not competitors.
Search — two legs into one ranked pool
Leg A (competitor-priority): per competitor domain, run people_search
company-scoped (company_domains) with the JD filters. Tag _source = competitor:<domain>.
Leg B (general): run the waterfall with the JD filters and no company scope. Tag
_source = general, over-provision ~1.4×N.
Merge + dedup by linkedin_url, rank competitor-sourced first, general fills the rest.
Enrich the profile — the work history qualify scores on
Run the linkedin_profile waterfall over the ranked pool → each row gets a normalized profile
with experience[{company, title, start, end, is_current, duration_months?, description?}],
newest role first. A row that comes back without employment history is a miss and costs nothing.
hyreflow enrich --input pool.csv --output enriched.csv --rows 0:1 \
--with '{"alias":"profile","tool":"linkedin_profile","payload":{"linkedin_url":"{{linkedin_url}}"}}'Field coverage varies by provider: the dated experience[] is the contract, while
about/skills/certifications are filled only by the providers that carry them — read an
absence as unknown, not as a gap. Skip rows that already carry a dated history (CRM/ATS records).
Qualify against the JD
Score each candidate's enriched history vs the JD must-haves → tier_1 | tier_2 | no_fit; drop
no_fit. This is the precision step (raw search is recall). hyreflow qualify reports whether each
candidate was scored on work_history or title_only, and refuses a batch in which no row carries
work history — enrich first, or opt in with --allow-thin-profiles (allow_thin_profiles: true over
the API) to rank on the sourcing fields.
Enrich the contact — candidate channel
Personal email + LinkedIn, NEVER work email. Run the personal_email waterfall
(see Integrations Overview for the provider order), first-hit;
verify deliverability (catch_all_safe counts as usable).
hyreflow enrich --input qualified.csv --output enriched.csv --rows 0:1 \
--with '{"alias":"email","tool":"personal_email","payload":{"linkedin_url":"{{linkedin_url}}"}}'Outreach (optional)
Personalize a first line, enroll in a recruiting sequence. See Campaign Plays.
Gates
Pilot one row, surface the projected enrich cost, get approval, then run the full pull. The two enrichment stages are the charged ones — search and qualify are free; a profile lookup that returns no employment history costs nothing. Cap the profile pass to the pool you intend to shortlist (~1.4×N).