SOURCE_ID: find_people NAME: Find People CATEGORY: List Building Build a static list of B2B people using job, location, and company-context filters. Imported records become available to your Floqer workflows for enrichment, scoring, outreach, and the like. INDEX: 1. Endpoints 2. Search model 3. Import lifecycle (one-time only) 4. Body shape (preview + create) 5. Filter fields catalogue 6. Dynamic options 7. How to configure end-to-end 8. Key notes 9. Where it fits 10. When to use ================================================================================ 1. ENDPOINTS ================================================================================ Source identifier (used in every endpoint path): `find_people`. POST /api/v1/sources/find_people/preview Scope: sources:read Returns up to 100 people that WOULD be imported for the given filters, without creating anything. Use this to validate filter combinations and inspect row shape before committing. POST /api/v1/sources/find_people Scope: sources:write Creates the source and queues an import of matching people (up to `max_count`). Returns `source_instance_id` (the new source's UUID). The import runs asynchronously. GET /api/v1/sources//data Scope: sources:read Paginated rows imported into the created source. `` is the UUID returned by Create. Query: `page_no` (default 1), `page_size` (default 20, max 200). Source-agnostic; see concepts.txt §10. POST /api/v1/sources//sync Scope: sources:write Connects the created source to a workflow and (by default) backfills it with the source's current rows. `` here is the UUID returned by Create. Body: { workflow_id, field_mapping, push_existing?, run? } — `field_mapping` keys are `input.` references on the target workflow, values are the source fields to pull. This step is source-agnostic; see concepts.txt §10 for the full shape. No connection is required. There is no dynamic-options endpoint for this source today (see §6). ================================================================================ 2. SEARCH MODEL ================================================================================ Every preview/create call is a filter-based people search. There is no "import mode" switch — you always pass a `filters` object. Filter groups: job job_level, job_department, job_title, include_related_job_titles, total_experience_months_min/max, current_role_months_min/max location (person's location) countries, region_country_codes, city_region_country company (person's current employer context) company_name, company_countries, company_region_country_codes, company_sizes, revenue_ranges, linkedin_categories OR google_categories OR naics_categories (pick ONE industry taxonomy — LinkedIn takes priority) contactability (optional booleans) has_email, has_phone_number, has_website Location mutual exclusion: Prefer `countries` OR `region_country_codes` for the person's location, not both. Same for `company_countries` vs `company_region_country_codes` on the company side. At least one key inside `filters` is required. An empty `{}` is rejected with 400. ================================================================================ 3. IMPORT LIFECYCLE (ONE-TIME ONLY) ================================================================================ Find People is always a one-time static import — there is no `pull_mode`, `schedule`, or recurring re-pull. max_count Caps how many people are imported on create. Default 100. Min 1, max 60000. Ignored on preview (preview always caps at 100 rows regardless of `max_count`). type Optional string; defaults to `"filters"` when omitted. Set `max_count` no higher than the `metadata.total_results` returned by the matching Preview call. ================================================================================ 4. BODY SHAPE (PREVIEW + CREATE) ================================================================================ Preview accepts: filters required: object with at least one filter key (see §5) type optional: string; defaults to "filters" on create; ignored on preview max_count optional: integer | string; ignored on preview Create accepts all the preview fields plus: name required: display name for the new source max_count optional: cap on rows imported; default 100 Example create body: { "name": "Senior Engineers in Canada", "max_count": 1000, "type": "filters", "filters": { "job_level": ["senior"], "job_department": ["engineering"], "countries": ["ca"] } } Preview response envelope: { "status": 200, "data": { "data": [ { "full_name": "Jane Doe", "job_title": "Director of Engineering", "company_name": "Acme Corp", "country_name": "Canada", "linkedin_url": "https://www.linkedin.com/in/jane-doe", ... } ], "metadata": { "total_results": 5432 } } } Rows are plain key/value objects (not `{label, value}` wrappers). Preview returns up to 100 rows; `metadata.total_results` is the total match count (best-effort). Create response envelope: { "status": 201, "data": { "source_instance_id": "", "name": "Senior Engineers in Canada", "created_at": "2026-05-26T12:00:00.000Z" } } ================================================================================ 5. FILTER FIELDS CATALOGUE ================================================================================ All filter keys live inside the `filters` object. ──────────────────────────────────────────────────────────────────── GROUP: job ──────────────────────────────────────────────────────────────────── job_level (string[]) Seniority buckets: "entry", "training", "non-managerial", "unpaid", "senior", "manager", "director", "vp", "partner", "cxo" Example: ["senior", "director"] job_department (string[]) Department names (lowercase). Example: ["engineering", "sales", "marketing", "finance", "product"] job_title (string[]) Specific job titles (free text). Example: ["Sales Lead", "Head of Marketing"] include_related_job_titles (boolean) When true with `job_title`, also match related titles. total_experience_months_min (number) total_experience_months_max (number) Total professional experience range in months. Example: min 24 (= 2 years) current_role_months_min (number) current_role_months_max (number) Time in current role, in months. ──────────────────────────────────────────────────────────────────── GROUP: location (person's location) ──────────────────────────────────────────────────────────────────── countries (string[]) ISO-3166 alpha-2 codes. Case-insensitive. Example: ["us", "ca", "gb"] region_country_codes (string[]) State/region codes in "cc-rr" form. Example: ["us-ca", "ca-on"] city_region_country (string[]) City strings: "San Francisco, US" or "Toronto, CA" ──────────────────────────────────────────────────────────────────── GROUP: company (current employer context) ──────────────────────────────────────────────────────────────────── business_id (string[]) Restrict to people at specific companies. Pass company IDs from a find_companies preview or import row (the `business_id` field on each company row). Example: ["0194a1b2-c3d4-7890-abcd-ef1234567890"] company_name (string[]) Employer name keywords. Example: ["Acme Corp", "Google"] company_countries (string[]) HQ country of the person's current company. Example: ["us", "ca"] company_region_country_codes (string[]) HQ region of the person's current company. Example: ["us-ca"] company_sizes (string[]) Employee-count buckets at the person's company: "1-10", "11-50", "51-200", "201-500", "501-1000", "1001-5000", "5001-10000", "10001+" revenue_ranges (string[]) Company revenue buckets (same values as find_companies). linkedin_categories (string[]) google_categories (string[]) naics_categories (string[]) Company industry taxonomy — use ONE per request. LinkedIn takes priority when multiple are sent. ──────────────────────────────────────────────────────────────────── GROUP: contactability ──────────────────────────────────────────────────────────────────── has_email (boolean) has_phone_number (boolean) has_website (boolean) Examples: Job + location preview: { "filters": { "job_level": ["senior"], "job_department": ["engineering"], "countries": ["ca"] }, "type": "filters", "max_count": 60000 } US sales leaders at mid-market companies: { "filters": { "countries": ["us"], "job_level": ["director", "vp"], "job_department": ["sales"], "company_sizes": ["201-500", "501-1000"] } } Output fields on each row: Preview row: prospect_id, business_id, full_name, name, first_name, last_name, professional_email_hashed, job_title, job_department, job_seniority, job_seniority_level, experience, skills, linkedin_url, linkedin, linkedin_url_array, company_name, company_website, company_linkedin, country_name, region_name, city (some rows additionally carry `interests`) Imported row (Get Source Data): the same public fields the preview returns, plus type, created_at ⚠ Email / phone are NOT returned. `professional_email_hashed` is an opaque hash, not a usable email. The `has_email` / `has_phone_number` / `has_website` flags in §5 are filter-only — they restrict the result set to rows the provider has contact data for, but the actual email / phone do NOT come back on the row. Filter-only fields — used to SELECT people, but do NOT come back on the returned/imported row: job_level (returned only as `job_seniority_level`), include_related_job_titles, total_experience_months_min/max, current_role_months_min/max, region_country_codes, business_id (the filter), company_name (the filter), company_countries, company_region_country_codes, company_sizes, revenue_ranges, linkedin_categories / google_categories / naics_categories, has_email, has_phone_number, has_website. If a downstream workflow needs the email / phone or rich firmographic context (industry, headcount, revenue, tech stack), chain a separate enrichment action after sync (e.g. an email finder, a phone waterfall, or `enrich_company_linkedin_profile`) — the list-builder rows themselves won't carry those values. ================================================================================ 6. DYNAMIC OPTIONS ================================================================================ No dynamic-options endpoint is registered for this source today. Filter values (job levels, departments, size buckets, etc.) are fixed enumerations — use the allowed values listed in §5 directly. ================================================================================ 7. HOW TO CONFIGURE END-TO-END ================================================================================ Step 1 — Preview before committing POST /api/v1/sources/find_people/preview Body: { "filters": { "job_level": ["senior"], "job_department": ["engineering"], "countries": ["ca"] }, "type": "filters", "max_count": 60000 } Check `data[]` and `metadata.total_results`. Iterate until happy. Step 2 — Create the source POST /api/v1/sources/find_people Body: { "name": "Senior Engineers in Canada", "max_count": 1000, "filters": { "job_level": ["senior"], "job_department": ["engineering"], "countries": ["ca"] } } Response: { "status": 201, "data": { "source_instance_id": "", "name", "created_at" }} Step 2b — (Optional) Poll imported rows while the backfill runs GET /api/v1/sources//data?page_no=1&page_size=20 ( = UUID from Step 2) Row keys match Preview. `total_count` grows until import finishes. Step 3 — Sync into a workflow POST /api/v1/sources//sync Example field_mapping: { "input.full_name": "full_name", "input.linkedin_url": "linkedin_url", "input.company_name": "company_name", "input.job_title": "job_title" } The initial import runs asynchronously after create returns. ================================================================================ 8. KEY NOTES ================================================================================ - No connection required. Unlike CRM sources, this endpoint does NOT return 424 for a missing integration. - Preview caps at 100 rows; `max_count` on preview is ignored. The preview row shape matches what create imports. - `max_count` defaults to 100 on create; hard max is 60000. - Company industry taxonomies are mutually exclusive (LinkedIn > Google > NAICS). - Preview rows are plain key/value objects (not `{label, value}` wrappers). - Search errors on preview: 402 (insufficient credits), 429 (rate limit), 502 (search service failure). On create the import runs asynchronously, so a failure there surfaces on the source's status rather than as a synchronous create error. ================================================================================ 9. WHERE IT FITS ================================================================================ DATA Job, location, and company-context filters evaluated when you preview or create. THIS SOURCE One-time static import of up to `max_count` matching people. WORKFLOWS Sync to a workflow for enrichment and outreach. ================================================================================ 10. WHEN TO USE ================================================================================ - Prospect by title + seniority: "Senior engineers in Canada" - Account-based people search: company_name + job_department filters - Firmographic + role ICP: company_sizes + job_level + countries For single-person enrichment by LinkedIn URL, use workflow actions (e.g. `scrape_person_linkedin_profile`) instead. ================================================================================ This file is maintained manually. Last updated: 2026-05-28. Full interactive reference: https://floqer.com/docs/reference