contacts
Search, browse, and manage your entire prospect base across campaigns, including LinkedIn search, enrichment, tagging, and lifecycle stages.
Instructions
Search, browse, and manage your global contact base.
One master record per person across all campaigns. View full interaction
history, add tags/notes, track lifecycle stages, and build reusable
prospect pools for future campaigns. Also search LinkedIn directly
for people without creating a campaign.
Args:
action: What to do:
"list" — List contacts with optional filters (default)
"search" — Search contacts by name, company, or title
"view" — View full cross-campaign history for one contact
"tag" — Add a tag (or remove with '-tag_name')
"note" — Add a note to a contact
"stage" — Update lifecycle stage
"stats" — Contact base dashboard stats
"export" — Export contacts as table, CSV, or JSON
"linkedin_search" — Search LinkedIn directly by name/company/title
"link" — Resolve a campaign's contact rows against the contact base
by name, so rows imported without a LinkedIn id pick one
up. Dry run unless dry_run=False.
"enrich" — Enrich contacts with full LinkedIn profiles + posts
"my_connections" — Search your 1st-degree LinkedIn connections (locally synced, guaranteed 1st degree)
query: Search text for 'search', 'linkedin_search', and 'my_connections' actions.
For 'enrich': search query to find contacts to enrich.
For 'linkedin_search' the query is passed to LinkedIn as KEYWORDS,
matched literally — a company name, a job title, a person's name, or a
combination such as 'Acme Corp CTO' or 'Jane Doe'. A natural-language
question ('who is the CTO of Acme?') is sent through unchanged and
usually comes back empty, so prefer keywords. Nothing is filtered out
locally. An empty result and a failed search are reported in different
words, so a "no matches" line means LinkedIn really returned nobody
rather than "the search broke".
The profile and posts fetches this triggers are paced: they used to go
out back to back and LinkedIn rate-limited them, so the call now spends
up to a fixed wall-clock budget waiting between fetches and prints how
much of it went on waiting. Expect tens of seconds.
contact_id: Global contact ID for view/tag/note/stage actions.
lifecycle_stage: Filter by stage (prospect/contacted/connected/engaged/customer/lost)
or target stage for 'stage' action.
tag: Tag to add/remove for 'tag' action, or filter for 'list'/'search'.
note: Note text for 'note' action.
min_fit_score: Minimum fit score filter (0.0-1.0).
limit: Max results to return (default 25). For 'linkedin_search' this is
capped at 25 per call because every result costs a profile fetch and a
posts fetch; a result list says so when your limit was capped.
format: Output format for 'export': 'table', 'csv', or 'json'.
campaign_id: Campaign whose contact rows to resolve, for the 'link' action.
match: How 'link' pairs campaign rows with contact base records. Only
'name' is supported (exact, ignoring case and extra spaces).
dry_run: For 'link' — True (the default) lists every row it would change
and writes nothing. Pass False to apply.
connected_since: For 'my_connections' — only people who became a
1st-degree connection on or after this date (YYYY-MM-DD).
connected_before: For 'my_connections' — only people who became a
1st-degree connection before this date (YYYY-MM-DD). Connections
synced before dates were recorded have no date and match neither
filter; the result line says how many those are.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | ||
| note | No | ||
| limit | No | ||
| match | No | name | |
| query | No | ||
| action | No | list | |
| format | No | table | |
| dry_run | No | ||
| contact_id | No | ||
| campaign_id | No | ||
| min_fit_score | No | ||
| connected_since | No | ||
| lifecycle_stage | No | ||
| connected_before | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |