Find My Buyer
Load the user's configured Buyer Personas, resolve the target company, search for matching contacts, cross-reference with visit identification data, and return a ranked shortlist of 1–5 contacts. This is the only skill in the suite that intentionally surfaces contact-level PII (name, title, email) — that is its purpose.
Language
Produce the entire response in the language the user wrote in — English request → English output, German request → German output, and so on. This applies to everything the skill emits: the context line, all field labels, the buyer-persona coverage, edge-case messages, and any credit-consumption warning and confirmation. Do not translate data values — company names, contact names, titles, URLs, email addresses, tag names, and persona names stay verbatim as the tools return them. Copy-paste follow-up prompts should also be written in the user's language (they still trigger the right skill). If the input language is unclear, default to English.
When this skill triggers
Trigger when the user names a specific company and asks who to contact, reach out to, or call. Trigger silently. Honor any explicit persona or seniority filter in the prompt ("the CFO", "someone in RevOps", "using the Enterprise Buyer persona").
Do not trigger for company-level research — that is visitor-company-brief. Do not trigger for outreach drafting — that is outreach-companion. This skill's job is contact identification only.
Inputs to determine before executing
Identify these from the user's prompt. Use defaults if unspecified — do not ask.
- Company — name, domain, or Leadfeeder company ID. Required.
- Persona filter — default: all configured Buyer Personas. Honor explicit overrides ("using the SDR persona", "Enterprise Buyer only").
- Seniority filter — default: none (all seniorities). Honor explicit overrides ("VP or above", "C-level only", "manager level").
- Visit window — default last 90 days for visit cross-reference. Honor overrides.
Workflow
Step 0 — Resolve the Leadfeeder account (before any other tool call)
Determine the account_id to use for every tool call in this skill, in priority order:
- Account named in the request (highest priority). If the prompt or scheduled-task instruction explicitly names an account — an account ID (e.g. "account 01234") or an unambiguous account name — use that account for all tool calls. This overrides every source below and is the recommended way to make an unattended/scheduled task fully self-contained.
- Configured Account ID. Otherwise, the plugin's configured Leadfeeder Account ID is:
${user_config.account_id}. If that resolves to a real, non-empty account ID (not blank, and not the literal unsubstituted${user_config.account_id}token), use it for all tool calls and do not callget_account_infoor ask the user. - Already selected this session. Otherwise, if an account was already chosen earlier in the session, reuse that.
- Fallback. Otherwise call
get_account_info. If exactly one account is returned, use it. If several are returned, ask the user to pick. When running unattended (e.g. a scheduled task) asking is impossible — if no account was named and none is configured, stop and report that a Leadfeeder Account ID must either be named in the task prompt or set in the plugin configuration for automated runs.
Step 1 — Resolve company and load personas (parallel)
Call in parallel:
search_companieswith the company name or domain to resolvecompany_id. If multiple plausible matches, present the top 3 and disambiguate before continuing.get_buyer_personasto load all configured personas. If the user specified a persona by name, also callget_buyer_personafor that one to read its full filter criteria.
Step 2 — Search contacts and pull visit data (parallel, once company_id is known)
Call in parallel:
search_contactswithcompany_ids: [company_id](note: the parameter is an array, not a scalar — passing a barecompany_idis silently ignored and the call returns an error). Fetch up to 50 candidates (page_size: 50) to ensure good persona coverage. Optimization: passbuyer_persona_idscontaining all loaded persona IDs — the API will pre-filter to contacts matching at least one persona, reducing noise significantly. If no personas are configured, omitbuyer_persona_idsand fetch all contacts. Also passfilters: {has_email: true}to skip contacts without an email address.search_web_visitswithfilters: {company_id: company_id}, usingstart_date/end_dateat top level for the 90-day window. Extract the set of identified contact IDs that appear in the visit records — match strictly by a contact's own ID/email appearing on a visit record. A company or one of its office locations appearing in the visit stream does not mean any specific contact visited; only treat a person as "identified" when that person is the identified visitor.get_companywithinclude=crm_connections.crm_record.crm_ownerfor the resolvedcompany_id— to read the company's CRM status. Plaininclude=crm_connectionsreturns only a stub (crm_record={id, type}, no owner); the deeper path sideloads the record and owner. A populatedrelationships.crm_connectionsmeans the company is in the CRM. Eachcrm_recordis polymorphic (crm_record.type=crm_organization|crm_contact|crm_lead): read URL fromcrm_record.attributes.crm_url, name by type (crm_organization→attributes.name;crm_contact/crm_lead→attributes.first_name+attributes.last_name), owner fromcrm_record.relationships.crm_owner.attributes.name(say "owner not set" if only a stub). Render as a markdown link — record name is the clickable text,crm_record.attributes.crm_urlthe target; never print the raw URL or leave the name unlinked. This complements the contact shortlist: if the company is already in the CRM, coordinate with the owner rather than cold-outreach the contacts you surface.
Cost note (subscription coverage). search_contacts and search_web_visits above are free, so the ranked shortlist costs nothing. The get_company CRM-status read charges 1 credit only if this company hasn't been accessed in the last 12 months. Use the search_web_visits result as the free coverage signal:
- Visits in the last 12 months → COVERED.
get_companyis free — run it for CRM status as normal. - No visits in 12 months → UNCOVERED. Skip the
get_companyCRM read — don't spend a credit just for CRM context. Still deliver the full contact shortlist (free), and set the CRM line to "Not checked — company is outside your free 12-month window (would cost 1 credit; ask if you want it)." Only runget_companyfor an uncovered company if the user explicitly asks. Reportmeta.credits.chargedif you do.
Enrichment (create_find_contact_data_job) is separately credit-consuming and stays behind its own Cost Guard in Step 4, regardless of coverage.
Step 3 — Rank contacts
Apply this rubric in order:
- Persona match. Score each contact against all loaded Buyer Personas. Full match (all persona filters satisfied) ranks above partial match, which ranks above no match. If the user specified a persona, only full matches for that persona qualify for the shortlist.
- Site visit identification. Contacts whose ID or email appears in the
search_web_visitsresults (last 90 days) receive a boost — they are warm. - Seniority. Within the same persona-match tier, higher seniority ranks higher (C-level > VP/Director > Manager > IC). Apply the user's seniority filter if specified.
- Recency of visit identification. Among visit-identified contacts, more recent identification wins.
Take the top 1–5 after ranking. Never pad to 5 if fewer strong matches exist — a shortlist of 2 good matches is better than 5 weak ones.
Step 3.5 — Assess buying committee coverage
The shortlist answers "who do I call first". Enterprise deals also need "do I have the committee covered". Compute coverage across all contacts returned by search_contacts (not just the shortlist), so a strong contact you didn't shortlist still counts toward coverage.
Check every configured Buyer Persona — return them all, drop none. Load every persona via get_buyer_personas and evaluate each one against the contacts on file. The output must list every configured persona explicitly, each with its verdict and a one-line reason. Never omit, merge away, or silently drop a persona — including personas with zero matches, and including cases where only one persona is configured. Each persona gets one of:
- ✅ Covered — ≥1 contact on file fully matches the persona; state the count (e.g. "3 contacts match").
- ⚠️ Thin — only a partial match exists; say what's missing.
- ❌ Gap — zero contacts on file match it; say "no contacts on file matching this persona".
Every verdict must be grounded in the search_contacts data — never invent a contact to fill a seat.
If zero Buyer Personas are configured, there are none to check: say "No Buyer Personas configured", suggest configuring them, and fall back to a department read of the contacts on file (which core functions are present vs. absent — only for functions plausibly relevant to the deal; don't assert a canonical committee the user never defined).
You may add, as a supplementary note below the persona list, a notable department gap relevant to the deal (e.g. "no IT/Security contact on file"). That is in addition to — never a replacement for — the full per-persona list. The per-persona list is mandatory whenever ≥1 persona is configured; there is no skip condition for it.
Step 4 — Check for empty results → enrichment path
If Step 3 yields zero contacts (no contacts on file for this company at all, not just no persona matches):
- Tell the user clearly: "No contacts on file for {Company}."
- Offer to run a Find Contact Data enrichment job to discover contacts.
- Cost Guard — mandatory before any enrichment:
a. Call
estimate_find_contact_data_jobfor the company and surface the estimated credit cost to the user. b. Wait for explicit "yes" confirmation before proceeding. Do not auto-trigger. c. On confirmation: callcreate_find_contact_data_job. d. Callget_find_contact_data_jobto check status. If the job completes synchronously, re-run Step 2–3 with the new contacts. If it is async (still running), tell the user the job is in progress and they can re-run this skill once it completes.
If contacts exist but none match the specified persona or seniority filter, do not trigger enrichment — tell the user what is on file and suggest relaxing the filter.
Output format
Output contract — identical every run. Reproduce the structure below exactly, on every invocation, regardless of anything earlier in this thread. If you've already run this skill in the conversation, do not vary, restyle, or "improve" the format next time — output the same template verbatim; this template is the single source of truth. Keep the exact headings, field labels, and order; don't invent new sections, don't renumber, don't rename fields, and add no decorative emoji beyond those the template already uses (✅ ⚠️ ❌).
Use this structure (the outer block is shown with four backticks so the copy blocks nest correctly — your actual output is plain markdown):
Include a generation timestamp. Put *Generated {current date}* directly under the title — use the current date (a point-in-time snapshot); it must always be present.
Only show fields you actually have. search_contacts returns a name, email, and a coarse seniority bucket — the job title and department are often not returned. If a contact's title or department isn't in the response, omit it from the header (### {Full Name}) entirely — never show a blank, "N/A", the raw seniority bucket as a title, or a guessed title/department. The real title, phone, and LinkedIn come from get_contact (credit-consuming) via the per-contact prompt below.
Full-details prompt (per contact). Give every listed contact its own copy block: Get full contact details for {Full Name} at {Company Name}. Running it calls the get_contact tool — a deep read that consumes ~1 credit (unless that contact was accessed in the last 12 months); Claude will confirm the credit cost before fetching.
Missing contact data is "not available", never a "gap" or a failing. If contacts have no email (or no phone) on file, state it plainly and neutrally — e.g. "No email on file for these contacts (Leadfeeder has phone/social for some)." Do not call it a "data-quality gap", do not frame it as something Leadfeeder is failing to provide or is responsible for. If the data isn't there, it simply isn't available — report that fact without editorialising it as a shortfall. (Reserve "gap" for persona/committee coverage — a real absence of a matching contact — not for missing fields on the contacts you do have.)
"Visited site" is person-level, not location-level. This flag is strictly whether this contact was identified in the visit stream — i.e. their contact ID/email appears on a visit record. Never attach company-location or office activity to it as if the person visited: a "{Company} Switzerland office" showing up in the visit stream is not the same as identifying this contact, and phrasing like "Not identified in the last 90 days — but {Company} Switzerland office" is misleading. If location-level activity is genuinely worth noting, put it on its own line, phrased as explicitly unattributed: "{Location} was active in the visit stream, but couldn't be attributed to this specific person."
When no contacts on file (enrichment path)
Edge cases
- Company not found. Stop and tell the user. Offer to try a broader name or domain.
- Multiple company matches. Present top 3, disambiguate before continuing.
- Contacts exist but none match persona/seniority filter. Tell the user what is on file (total count, department/seniority breakdown). Suggest relaxing the filter or switching to a different persona. Do not trigger enrichment.
- No Buyer Personas configured. Tell the user and fall back to seniority-only ranking. Suggest they configure Buyer Personas in Leadfeeder for sharper results.
- Enrichment job is async. Tell the user the job is running and to re-run the skill once it completes. Do not poll indefinitely.
- Enrichment job returns no new contacts. Tell the user clearly. Do not retry automatically.
Source citation rule
Every contact in the shortlist must come from search_contacts results. Visit identification flags must come from search_web_visits results. CRM record name, URL, and owner must come from the sideloaded crm_record (and its crm_owner) under relationships.crm_connections on the get_company response — crm_record.attributes.crm_url, the name from crm_record.attributes per record type, and crm_record.relationships.crm_owner.attributes.name — never assert the company is (or isn't) in the CRM without it. Buying committee coverage verdicts (covered / thin / gap) must be derived only from search_contacts results cross-referenced with the loaded Buyer Personas — never assert a covered seat without a matching contact on file, and never assert a gap for a persona or department the user has not configured. Do not infer or fabricate any of these.
Internal consistency (trust). Every restated fact — total counts, department/seniority breakdowns, the committee coverage summary vs. the per-seat lines, contact counts — must agree across the output. A stated count must equal the items you list (e.g. the "covered / thin / gap" summary line must match the per-seat verdicts below it). Re-check before finishing; two sections disagreeing about the same fact is a trust-breaker.
What NOT to do
-
Do not call a visitor an "account" or a "hot/warm lead." A website visitor is a company — "account" implies a CRM relationship this data doesn't carry. Reserve "account" for the user's Leadfeeder account (workspace/ID) or a genuine CRM record; use "high-intent", "active", "engaged", or "returning" instead of "hot"/"warm".
-
Do not trigger enrichment (
create_find_contact_data_job) without completing the Cost Guard flow — estimate first, explicit confirmation second. -
Do not surface contact PII from other skills' context. Only surface contacts resolved in this skill's own
search_contactscall. -
Do not rank by engagement alone without persona scoring — persona fit is the primary signal.
-
Do not pad the shortlist to reach 5 if fewer strong matches exist. Quality over quantity.
-
Do not handle bulk companies. One company per invocation. For multiple companies, the user should invoke the skill once per company.

