---
name: find-leads-v2
description: Canonical approval-gated campaignless lead sourcing state machine.
visibility: internal
---

# Find Leads V2

This is the canonical campaignless workflow. The durable identity invariant is
`runId = leadListId`. Resume from product state with `get_find_leads_run`; never
reconstruct progress from chat memory.

## Prerequisites and recovery

- Call `get_auth_status`. If login is required, use `start_cli_login` and
  `wait_for_cli_login` before retrying.
- Require an active workspace. If none is active, use `list_workspaces` and
  `set_active_workspace`.
- If lifecycle tools, this prompt, or its flow asset are missing/outdated,
  update the Sellable MCP/plugin, restart or reload the host, and retry.
- Sales Nav requires a connected LinkedIn account with an active Sales Navigator search session.
- Prospeo requires configured access and enough Prospeo credits for the bounded
  plan.
- Signal Discovery capacity must be available; otherwise wait or choose one of
  the other providers.
- For hiring-led targeting, start with `Prospeo` because its job-posting filters
  are native. Run source work in the parent thread; do not dispatch custom source-scout subagents.

## Durable flow

1. `bootstrap_find_leads({ bootstrapKey, name })`, then show the returned watch
   URL. On restart call `get_find_leads_run({ runId })`; if the link expired use
   `reissue_find_leads_watch_link`. A deleted run/list is terminal: start a new
   run only after explaining that recovery.
2. Draft a compact source plan: target, provider, filters/signals, sample size,
   import cap, fallback, and expected provider cost/capacity. Ask for explicit
   source-plan approval. Persist it with `update_find_leads_run` action
   `approve_source_plan`. This source-plan approval permits search only.
3. Choose exactly one provider. Load `get_provider_prompt` for the selected provider only,
   then call `preflight_find_leads_provider` with the current
   run version and prompt/workflow hash. A prompt loaded for another run,
   provider, or memory wildcard is invalid.
4. Search/sample through `search_sales_nav`, `search_prospeo`, or
   `search_signals`, always passing `findLeadsRunId` and current `runVersion`.
   For Signal, persist the approved post selection with
   `select_promising_posts` using the same run and tab.
5. Present the bounded import plan and ask for a distinct import approval.
   Persist `approve_import`; never treat source-plan approval as import approval.
6. Call `import_leads` with exactly one target: `findLeadsRunId`. Pass the
   current run version and provider search/tab receipt. Never pass a campaign
   target on this path.
7. Call `wait_for_lead_list_ready({ findLeadsRunId })` until ready, empty,
   failed, or cancelled. Re-read the snapshot after timeout/retry.
8. Review rows with `get_rows_minimal({ tableId: runId })`. For terminal empty
   runs, export remains valid and returns a header-only CSV.
9. Export with `export_table_csv({ tableId: runId })`. Do not create a second
   export tool. Export failure must not mutate the run.

An optional Add to Campaign action is only a suggestion after review when the
user explicitly asks. It is never an automatic MCP step.

## Terminal states

- `failed`: show stable code, retryability, and exact next action. Resume the
  same run when retryable.
- `cancelled`: do not search or import again; review/export existing rows.
- `ready` or `empty`: review and export.

Progress extensions and background task protocols are optional host hints, not
dependencies. Ordinary registered tools and the durable snapshot are enough.
