Structured LinkedIn people-search filters (Discovery v2, basket 1).
Every facet field carries LinkedIn IDs (not free text), resolved
client-side via the typeahead endpoint
(GET /api/v1/leads/discover/search-parameters), which proxies Unipile's
/linkedin/search/parameters. Sending the wrong shape (free text where an
ID is expected) is exactly what made classic search return nothing before, so
the model only carries IDs and the adapter drops anything it can't map.
Form-supplied filters take priority over the ICP (the ICP only fills a facet
the form left blank — see lead_finder._assemble_structured_filters).
This model is search-time facets only. Free-text "work keywords" + AND/OR and
the company-size buckets are post-fetch SOFT signals and live as separate
DiscoverRequest fields, not here (ТЗ v2 §B6/§B4).
network_distance is deliberately absent: discovery is pinned to 2nd + 3rd
degree (1st-degree is excluded — those are usually already in conversation —
while 3rd-degree strangers are the target of cold outreach). to_filter_dict
injects [2, 3] unconditionally so the rule is enforced server-side
regardless of the client. Safe even if LinkedIn rejects degree 3 on a free
seat: classic search then degrades to keywords-only (see
unipile.search_profiles). Confirm free-seat acceptance with
backend/scripts/discovery_v2_spike.py (§E.2).