<summary>Query your canonical people (`person_profiles`) — one deduped row per real person in your
account: everyone you've found, enriched, or reached out to, deduped across every campaign and
list. Use this for "is <name> someone we know",
"people who are VPs at fintech companies", or any question about the person themselves. For a
person's outreach progress — which campaigns they're in, what stage — use query_prospects
instead: a prospect is a per-owner tracking row, so the same real person can have several
prospect rows; query_people collapses those to one canonical entity.
Columns: id, display_name, title, headline, company, location,
summary, linkedin_url, linkedin_provider_id, email, connections_count, follower_count,
is_open_profile, is_premium, work_experience (JSONB), education (JSONB), company_profile_id,
owner_email, data (JSONB), created_at, updated_at. `company_profile_id` is the person's
canonical employer FK (NULL when unresolved). Sliq's CRM is deals and tasks: to see whether
someone has a deal or a task, use query_deals (status='all') / query_crm_tasks with
person_id.
`connections_count` / `follower_count` are NULL when never fetched (not 0); `is_open_profile` /
`is_premium` are NULL likewise. JSONB queries: `data->>'some_key' ILIKE '%...%'`.
`work_experience` and `education` are the person's full LinkedIn employment and education
history — the same arrays enrich_linkedin_profiles returns, already stored on the person from
the last profile lookup, so reading them here costs nothing (answer "where did they go to
school", "are they an LSU alum", "how long in seat" from these instead of a fresh enrich).
Each is a list; an empty list means it was never captured for that person. They arrive in
LinkedIn display order (NOT sorted by date). These arrays are large for senior people — a
broad `limit=200` read that returns them can exceed the 50KB direct-return cap and truncate;
for a wide pull, either narrow the `where_clause` or call this from `run_code` (nothing
truncates there) and print only the fields you need.
To list the outreach prospects tracking a person, take an `id` from here and call
query_prospects with `where_clause="person_id = <id>"`.
Each row also carries `segments`: the person's segment tags across every segment group they're
classified into — [{group_id, group_name, tag}], `tag` 'No match' where the classifier couldn't
place them (empty when no group has classified them). Segments are the LLM-defined
people dimensions from the Explore Segments surface (e.g. Seniority -> VP); manage them with the
segment tools (list_segment_groups etc.).
Pass `group_by` for per-bucket counts over ALL your people instead of a row list — a whole-set
aggregate, never capped at the 200-row `limit`, so it answers "how many people per <dimension>"
without paging. `outreach_stage` buckets each person by their most-advanced lead-funnel stage
across every campaign; `segment` buckets each person by their tag in one segment group (pass
that group's id as `segment_group_id`). Both ignore `where_clause`.</summary>
<returns>
<description>In row mode (group_by omitted) — a dict with count, truncated, and items array (each row
{id, display_name, title, headline, company, location, summary, linkedin_url,
linkedin_provider_id, email, connections_count, follower_count, is_open_profile,
is_premium, work_experience, education, company_profile_id, owner_email,
segments: [{group_id, group_name, tag}], data, created_at, updated_at}).
In aggregate mode (group_by set) — a dict {group_by, groups} where groups is a list of
{key, count} ordered by count descending; for "segment" the keys are tag names, 'No match'
for people the classifier couldn't place, and '' for people the group hasn't classified.</description>
</returns>