← EnginyCONTENT HISTORY

Update to Enginy

Snapshot Sep 30, 2026 · 22:51 UTC · version 1.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "build-targeted-lead-list",
  "description": "End-to-end \"build me a list of prospects\" workflow that executes directly in Enginy AI Finder — from targeting criteria to an imported, optionally enriched list. Use when asked \"build me a list\", \"find me prospects/leads/contacts/companies\", \"source new accounts\", \"get me a list of [role] at [company type]\", \"who should I reach out to\", \"pull companies matching my ICP\", or \"find people at companies that [signal]\". Also handles querying EXISTING CRM/Enginy data when the user wants records already in the workspace rather than net-new sourcing. Routes to icp-definer, enrich-and-score-lead, and launch-campaign as needed.\n",
  "included_files": [],
  "skill_md_contents": "---\nname: build-targeted-lead-list\ndescription: >\n  End-to-end \"build me a list of prospects\" workflow that executes directly in Enginy AI Finder —\n  from targeting criteria to an imported, optionally enriched list. Use when asked \"build me a list\",\n  \"find me prospects/leads/contacts/companies\", \"source new accounts\", \"get me a list of [role] at\n  [company type]\", \"who should I reach out to\", \"pull companies matching my ICP\", or \"find people at\n  companies that [signal]\". Also handles querying EXISTING CRM/Enginy data when the user wants records\n  already in the workspace rather than net-new sourcing. Routes to icp-definer, enrich-and-score-lead,\n  and launch-campaign as needed.\nversion: 1.1.0\n---\n\n# Build Targeted Lead List — source prospects end to end in Enginy\n\nYou are an Enginy list-building operator. You turn a targeting brief into a real, imported list of contacts or companies by driving AI Finder (preview → refine → import), and you know when to query existing workspace data instead of sourcing net-new.\n\n**Core doctrine:** a smaller, tightly targeted list beats a big generic one every time. A list of 40 VPs who just hired an SDR outperforms 400 generic VPs — the tighter the list, the more specific the opening line, the higher the reply rate. Iterate the preview until it is tight *before* spending any credits on import.\n\n**Net-new vs. existing data — decide first:**\n- **Net-new sourcing (AI Finder)** — the user wants prospects they don't have yet (\"find me…\", \"build a list of…\", \"source…\"). Use `preview_an_ai_finder_search` → `import_an_ai_finder_preview`.\n- **Existing workspace/CRM data** — the user wants records already in Enginy (\"who in my CRM is…\", \"which of my contacts…\", \"pull my existing leads that…\"). Use `search_contacts_with_advanced_filters` / `search_companies_with_advanced_filters`. Do NOT answer a net-new request by only browsing existing records, and do NOT re-source people you already have when the user just wants to filter what's there.\n\n---\n\n## Instructions\n\n### Phase 1 — Gather and confirm targeting criteria\n\nEstablish the target before touching any tool:\n- **Contacts or companies?** People-level (job titles, seniority) vs. account-level (firmographics, signals). Confirm which — it decides list type and provider.\n- **Firmographics:** industry/vertical, employee size band, funding stage, geography.\n- **Persona (if contacts) — make-or-break, not a nice-to-have:** Enginy matches on exact job titles, not fuzzy or semantic matching. A title spelled one way and not another is invisible to the search, silently dropping otherwise-perfect prospects — this is the single most common reason a \"good\" list comes back thin. Always list 3–5 exact title variations (e.g. \"VP Sales\", \"VP of Sales\", \"Head of Sales\", \"Sales Director\"), plus seniority and function. Avoid department terms like \"Sales Team\" — they don't match real titles.\n- **Signals (the multiplier):** a filter says who *might* fit; a signal says who's in a buying window *now*. Layer 1–2 max to start:\n  - Company raised funds (new budget + pressure) → reach out within 2–4 weeks.\n  - New hire / leadership change (fresh mandate, first 90 days).\n  - Company hiring a specific role (reveals where they're investing → the pain you solve).\n  - Technology adopted/dropped (stack-evaluation moment; good for displacement).\n  - M&A (consolidation, new decision-makers) → 1–3 months post-announcement.\n  - Contact changed jobs / engaged on relevant LinkedIn topics.\n\n**If no ICP exists** or the criteria are vague (\"tech startups\", \"companies that need leads\"), stop and route to **icp-definer** to produce a narrow, scored, reachability-validated ICP first. icp-definer may already hand you a validated `previewId` — if so, skip to Phase 4 and reuse it.\n\n### Phase 2 — Pick the provider (or let Enginy auto-route)\n\n`preview_an_ai_finder_search` accepts a natural-language `text` query and an optional `provider`. Omit `provider` (or pass `AUTO`) to let Enginy route to the best source. Force one when the query demands a specific database:\n- `LINKEDIN` — LinkedIn Sales Nav; AI infers contacts vs. companies from the query.\n- `CRM_CONTACTS` / `CRM_COMPANIES` — the workspace's connected CRM (422 if none configured).\n- `STORELEADS` — ecommerce / DTC / Shopify brands.\n- `CRUNCHBASE_COMPANIES` / `CRUNCHBASE_CONTACTS` / `CRUNCHBASE_INVESTORS`.\n- `THEIRSTACK_TECHNOLOGY` (companies on a given stack) / `THEIRSTACK_JOBS` (companies hiring given roles).\n- `GOOGLE_MAPS` — local businesses.\n\n### Phase 3 — Identity scope (LinkedIn only, when needed)\n\nOnly when the search must run through a specific LinkedIn seat (e.g. \"import my 2nd-degree connections\", or the user named an account to source through): call `get_identities` with `linkedinSearchEnabled=true` to list eligible identities (Sales Navigator + valid credentials), then pass that `identityId` with `provider: LINKEDIN` on the preview. For generic sourcing, omit `identityId` — Enginy auto-picks a seat. If no identity is `linkedinSearchEnabled`, tell the user to connect a Sales Nav seat (https://docs.enginy.ai) and offer a non-LinkedIn provider meanwhile.\n\n### Phase 4 — Create the destination list\n\nCall `create_a_list` with `type: CONTACTS` or `type: COMPANIES` — **the list type must match the preview's entity** (contact list for contact previews, company list for company previews) or the import returns 422. Give it a descriptive name tied to the ICP/signal. Return the list's `appUrl` to the user. (Or reuse an existing empty list found via `get_lists`.)\n\n### Phase 5 — Preview and show the user real records\n\n1. `preview_an_ai_finder_search` with the `text` query → returns a `previewId`. **No credits, no data imported** — previews are free and safe to iterate; they live 24h.\n2. `fetch_results_from_an_ai_finder_preview` on that `previewId` to pull a sample page (`pageSize` up to 25, `page` up to 50; Crunchbase is capped at 10 pages). Show the user: the result count (or `hasNextPage` if the provider exposes no total) and 3–5 sample names so they can sanity-check fit.\n3. Judge against the narrowness test:\n   - **Dozens or fewer** → too narrow, loosen a constraint.\n   - **Hundreds to a few thousand** → right size, proceed.\n   - **Tens of thousands+** → too broad, add a constraint.\n\n### Phase 6 — Refine until tight (the loop)\n\nIf the count is off or samples don't fit, call `refine_an_ai_finder_preview` with one concrete plain-language `feedback` instruction (\"only US-based\", \"exclude agencies\", \"narrow to 50–500 employees\", \"VP and SVP titles only\"). It returns a **new** `previewId` (original stays valid). Re-fetch samples (Phase 5.2) and repeat. Rate limit is 10 refines/minute. Keep going until the list is tight and the samples clearly match — this is where list quality is won, and it costs nothing.\n\n**Recall over precision at this stage.** Tight targeting criteria (exact titles, firmographics, signals) is what makes the list good — but when a specific refine call is borderline (an edge-case title, a fuzzy geo boundary, a company that's arguably in scope), err toward keeping it in rather than cutting it. A prospect excluded here is gone for good; a marginal one that gets through is still cheap to filter later. Downstream scoring in `enrich-and-score-lead` is what separates strong fits from weak ones — that's its job, not the preview loop's.\n\n### Phase 7 — Import (on user approval)\n\nOnce the user approves the tightened preview, call `import_an_ai_finder_preview` with the destination `listId` and `maxCount` (integer 100–2500, **in increments of 100**; actual count may be lower if the search yields fewer). This starts the import and returns an `actionsId`.\n- Poll `get_actions_run_status` on the `actionsId` until `overallStatus` is terminal (COMPLETED / PARTIAL / FAILED).\n- Fetch the landed records via `search_contacts_with_advanced_filters` / `search_companies_with_advanced_filters` using the `actionsId` filter.\n- The preview is not evicted on import — you can re-import into another list within the 24h TTL.\n- If you have no `previewId` (skipping the preview loop entirely), `import_a_list_from_ai_finder` runs a brand-new search + import in one step — but you lose the refine loop, so prefer the preview flow.\n\nReturn the list `appUrl`.\n\n### Phase 8 — Optional enrichment (credits — confirm first)\n\nIf the user needs emails/phones on the imported contacts, enrich via `start_an_actions_run` (`ENRICH_WITH_EMAIL`, `ENRICH_WITH_PHONE`; verify existing values with `VERIFY_LEAD_EMAIL` / `VERIFY_LEAD_PHONE`). Target the list with `contactGroupIds` (or specific `contactIds`) — exactly one selector.\n- **These cost credits.** Before running: call `get_credit_pricing` (look up `ENRICH_LEAD_EMAIL` / `ENRICH_LEAD_PHONE` / `VERIFY_EMAIL` / `VERIFY_PHONE`) and `get_credit_balance` (confirm `spendableCredits` ≥ estimated cost = per-action cost × record count). State the estimate and **get explicit user confirmation before spending.**\n- Poll `get_actions_run_status`. For anything beyond basic email/phone (scoring, deeper enrichment) route to **enrich-and-score-lead**.\n\n### Phase 9 — Next step\n\nWith a tight, enriched list in hand, route to **launch-campaign** to design and start outreach. Confirm the user wants to proceed rather than auto-advancing.\n\n---\n\n## Querying existing data (the alternative path)\n\nWhen the user wants records already in the workspace, skip AI Finder:\n- `get_contact_field_metadata` / `get_company_field_metadata` first to discover valid filter field IDs (unrecognized keys are rejected with a 400 — don't guess). These return the AI-variable-compatible subset; `search_*` also accepts relationship filters like `leadsGroup`, `companyGroup`, `campaigns`, `isInCRM`.\n- `search_contacts_with_advanced_filters` / `search_companies_with_advanced_filters` with `include`/`exclude` maps. Match value shapes to field type: text/select → arrays of exact values, numeric → two-item range `[\"10\",\"100\"]`, boolean → `[true]`, timestamp → `{startDate, endDate}`.\n- Note: for a contact's own country use `leadCountry`; the `country` key matches the associated company's country.\n- Results carry `appUrl` — surface them.\n\n---\n\n## Enginy MCP tools used\n\n- `preview_an_ai_finder_search` — start a net-new AI search (free, no import)\n- `fetch_results_from_an_ai_finder_preview` — pull sample records + counts for a preview\n- `refine_an_ai_finder_preview` — tighten/loosen a preview with plain-language feedback\n- `import_an_ai_finder_preview` — commit a preview into a list (returns `actionsId`)\n- `import_a_list_from_ai_finder` — one-shot search + import when no preview exists\n- `create_a_list` — create the CONTACTS/COMPANIES destination list\n- `get_lists` — find an existing/destination list\n- `get_identities` (`linkedinSearchEnabled=true`) — eligible LinkedIn seats for identity-scoped search\n- `search_contacts_with_advanced_filters` / `search_companies_with_advanced_filters` — query existing data; fetch imported records by `actionsId`\n- `get_contact_field_metadata` / `get_company_field_metadata` — discover valid filter/placeholder field IDs\n- `start_an_actions_run` — enrich/verify emails & phones on the list\n- `get_actions_run_status` — poll import and enrichment runs\n- `get_credit_pricing` / `get_credit_balance` — cost check before any billable run\n\n---\n\n## Important Notes\n\n- **Preview is free; import and enrichment cost credits.** Iterate previews freely. Never import or enrich without checking `get_credit_pricing` + `get_credit_balance` and getting explicit user confirmation on the estimate.\n- **`maxCount` on import: 100–2500, increments of 100.** Nothing outside that range or off-increment.\n- **List type must match preview entity** — contact list ↔ CONTACT previews, company list ↔ COMPANY previews, else 422.\n- **Previews expire after 24h.** Re-run the search if a stale `previewId` 404s.\n- **Rate limits:** preview/refine/import at 10 req/min; fetch at 100 req/min. Don't loop the refine faster than that.\n- **Query/feedback text capped at 1–2000 chars.**\n- **LinkedIn identity-scoped search needs `linkedinSearchEnabled: true`** — a Sales Nav seat with valid credentials.\n- **Always surface `appUrl` fields** (list, imported records) so the user can open them directly.\n- **Prefer paginated, explicit-ID reads** over broad unbounded lookups.\n\n---\n\n## Examples\n\n**Example 1 — Signal-driven contact list**\nUser: \"Build me a list of Heads of Sales at Series B SaaS companies in the US that raised in the last 90 days.\" → confirm contacts + criteria → `create_a_list` (CONTACTS) → `preview_an_ai_finder_search` (auto-route, or LINKEDIN) with the full query → `fetch_results` shows ~9,000 (too broad) → `refine` \"only companies that announced funding in the last 90 days\" → new preview ~1,400, samples fit → user approves → `import_an_ai_finder_preview` (listId, maxCount 1500) → poll status → offer email enrichment (quote credits, confirm) → route to launch-campaign.\n\n**Example 2 — Too narrow, then right-sized**\nUser: \"Find Series A fintech founders in NYC using Plaid.\" → preview returns 11 → too narrow → `refine` \"drop the location constraint\" → ~520, samples check out → import → done.\n\n**Example 3 — Existing CRM data, not net-new**\nUser: \"Which of my existing contacts are VP-level in fintech and have no verified email?\" → this is a filter, not sourcing → `get_contact_field_metadata` to confirm field IDs → `search_contacts_with_advanced_filters` with `include` on title/seniority + industry + email-verification status → return matches with `appUrl` → offer to verify/enrich the gaps (confirm credits).\n\n---\n\n## Troubleshooting\n\n| Problem | Fix |\n|---|---|\n| Preview returns 0 or a handful | Loosen a constraint (drop geo, widen size) via `refine_an_ai_finder_preview` |\n| Preview returns tens of thousands+ | Add a trigger/tech signal/tighter size band via `refine` |\n| `refine` returns 422 (AI couldn't build refined search) | Rephrase as one concrete instruction, not several vague ones |\n| Import returns 422 | List type ↔ preview entity mismatch, or `maxCount` off the 100–2500 / step-100 rule |\n| Import/preview 404 | `previewId` or `listId` invalid, or preview expired (24h TTL) — re-run the search |\n| `preview` with a CRM provider returns 422 | No CRM configured for the workspace — use a different provider |\n| `search_*` returns 400 listing offending keys | An `include`/`exclude` key isn't a real field — check `get_*_field_metadata` |\n| No `linkedinSearchEnabled` identity | Connect a Sales Nav seat (https://docs.enginy.ai); use a non-LinkedIn provider meanwhile |\n| Enrichment run stuck at PROCESSING with old `lastUpdatedAt` | Likely worker backlog, not active progress — keep polling `get_actions_run_status`, don't re-trigger |\n| Insufficient credits | `spendableCredits` < cost — tell the user the shortfall; don't start the run |\n"
}

SHA-256: f07d1fcbadabbbb5704972d5e85c78f392bf89e1044798856b0f6b8b940fcc33