leadbay_prospecting_overview — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited leadbay_prospecting_overview (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You keep your own memory of how this user likes to work — tone, naming, formatting, what they ask you to skip. Leadbay does not store that and does not need to.
What Leadbay does need is anything that changes who it should find. When the user states targeting criteria in conversation ("I target fleets over 100 vehicles", "carriers are a bad fit unless they do last-mile delivery", "climate engineering is also my market"), call leadbay_refine_lead_targeting so it changes what Leadbay surfaces for the whole org and on every future refresh — not just this conversation. When they say a specific lead is wrong for them, record the dislike with their words as its reason rather than noting it.
You are working with Leadbay through the leadbay_* MCP tools. This prompt orients you to the user's mental model so you don't re-discover the workflow each session.
These rules apply to every Leadbay workflow that calls leadbay_pull_leads, leadbay_bulk_qualify_leads, leadbay_research_lead_by_id, leadbay_import_and_qualify, or leadbay_enrich_titles. Treat timeouts and stream-closed errors as transient, not as signals to replan.
After your first leadbay_pull_leads call, capture response.lens.id into your working memory and pass it explicitly as the `lensId` argument to every subsequent call in this session — including any re-pulls, bulk qualifies, or research calls that accept it. (Field-name caveat: the response nests it as lens.id; the parameter on subsequent calls is lensId.) The active lens can shift between calls (5-minute client cache + backend last_requested_lens can change if the user touches the web UI). A lens shift mid-workflow throws away your top-10 work.
leadbay_bulk_qualify_leads and leadbay_import_and_qualify accept wait_for_completion:false and return immediately. They hand back different ids: bulk_qualify_leads returns {status:'running', notification_id, lead_ids, lens_id} — poll leadbay_qualify_status with those. import_and_qualify returns {status:'running', import_ids} and no notification_id at all — poll leadbay_import_status({importIds, dry_run}) with those. Check it until it completes, per A LAUNCHED JOB below. Use the async pattern by default — the blocking default can exceed the MCP client's per-call timeout on large batches and produce a misleading "Request timed out" even though the server is still working.
leadbay_research_lead_by_id fan-outleadbay_research_lead_by_id is composite and reads many sub-resources. Calling it on 10 leads in parallel can saturate the transport and produce "Tool permission stream closed" errors that look like permission failures but are really backpressure. Call it sequentially, or at most 3 in parallel. If one call fails with a stream/timeout error, retry that one call once before moving on; on a second failure, note the lead and continue — do not abandon the remaining leads.
If a Leadbay tool returns "Request timed out", "stream closed", or any other transport-level error (distinct from a Leadbay-issued error payload), the work may still be running server-side. Do this in order:
wait_for_completion:false and poll the status tool with the returned id. Don't re-pull leads; that can shift the lens.If pull_leads itself fails and you have no prior batch, then yes — retry it, explicitly pass the lensId you captured (if any), and continue.
Leadbay has no cancel. Once leadbay_enrich_titles, leadbay_bulk_qualify_leads, leadbay_import_leads or leadbay_import_and_qualify has returned a launched or running result, that work is queued on Leadbay and runs to completion, and the quota it uses is already committed. A discovery, preview or dry_run result launched nothing and is not covered here.
The user cancelling in the chat, a request timeout, or a closed stream stops YOUR waiting, never the job. cancelled: true means we stopped watching, not that the work stopped. What to do next depends on what you are holding:
handle covers a second time — that uses the quota again on the same rows. leadbay_import_status takes importIds, so pass the values of import_ids under that name. A qualification started by leadbay_import_and_qualify has no notification of its own: resume it with leadbay_qualify_status({lead_ids, lens_id}).
failed[] entrieswith error:"not_queued", or a rows_pending_upload count. Poll the handle for what was launched and re-run for that subset only, never for the whole batch.
returned. Check leadbay_account_status first: the launch may have landed and finished. Calling the same tool again with the same arguments will usually hand back the job already launched rather than starting a second one, but that guard is in-memory, five minutes, and per process, so it is best-effort — say what you are about to re-run before you use the user's quota on it.
A result saying the job is still working (still_running, next_poll, a running status) holds only ids to check it with. If the user's request needs the output:
does not look broken.
finished: wait_seconds: 45 where accepted, else every 15–30 s. A stop_reason or an early flat count is not finished.
Calling the launcher again is not a check: it can charge twice. Stop early only if the user said not to wait or the check tool says the job stalled. Then say it is not ready, show what landed, and that asking again fetches the rest.
Leadbay supports two parallel ways to find leads to act on. Detect which entry the user wants from their natural language, then route accordingly.
Discovery entry Follow-up entry
───────────────── ───────────────
leadbay_pull_leads leadbay_pull_followups
(new from the Discover (re-engagement on the
wishlist, lens-driven) Monitor view of known leads)
│ │
wrapped by: wrapped by:
leadbay_daily_check_in leadbay_followup_check_in
│ │
│ optional: │ filters by user phrasing:
│ research_lead_by_name_fuzzy / │ geo, sector, recency,
│ research_lead_by_id │ liked / pushback / outcome
│ (deepen profile) │
└───────────┬────────────────┘
▼
leadbay_prepare_outreach
(single-contact brief)
│
▼
user does outreach
(agent drafts email / call / DM)
│
▼
leadbay_report_outreach
(close the loop — verification + outcome)Discovery signals: "show me my leads", "what's new today", "any new prospects", "let's prospect", no mention of prior context. Route to leadbay_pull_leads.
Follow-up signals: "what should I follow up on", "leads I haven't contacted", "leads in [city]", "before my trip", "this week", "this month", "what's overdue", explicit mention of recent or pending actions. Route to leadbay_pull_followups — the Monitor view of known leads. Apply set_filter for geo / sector / recency / action-type refinement; the filter is server-persisted across sessions.
When in doubt, ask. The two paths return overlapping but differently-ranked data; presenting the wrong one wastes the user's time.
Routing the user's first message to an entry point (orchestrator prompts):
leadbay_daily_check_in (Discovery orchestrator — wraps leadbay_pull_leads)leadbay_followup_check_in (Follow-up orchestrator — wraps leadbay_pull_followups)Never call leadbay_pull_leads directly for a follow-up query. Never call leadbay_pull_followups for a discovery query. The two tools read from different backend tables; iterating pages of one to fake the other is a known failure mode (see the anti-confusion guardrail in pull_followups's description).
After leadbay_prepare_outreach returns a brief, the agent drafts. Adapt to the user's connected outreach tools — these change the draft idiom:
| Tool | Channel strength | Draft idiom |
|---|---|---|
| Lemlist | Email + LinkedIn + WhatsApp + cold call | Sequence step (subject + body + step-N timing) |
| Outreach.io | Email + call cadence | Sequence step; surface intent signals for forecasting |
| Salesloft | Email + call + LinkedIn cadence | Cadence step; pair with deal context if available |
| Apollo | Email-first | Clean cold email; include prospect signal references |
| HubSpot Sales Hub | Email + tasks | HubSpot sequence email; recommend a task type |
| Instantly | Email at scale | Deliverability-conscious email (<80 words, no link spam) |
| Attio | Email + LinkedIn from CRM | Outreach record on the Attio person; reference the deal |
| Amplemarket | 7 channels (email/LI/call/SMS/WA/voice/video) | Per-channel variants; suggest the strongest channel |
| Generic / Gmail / Outlook | Clean copy-paste email; no tool-specific syntax |
Detect the active tool in this priority order:
IRON LAW — OUTCOME AFTER OUTREACH. The moment the user reports outreach happened ("I sent it", "she didn't pick up", "left a voicemail", "they replied", a forwarded email thread, a calendar invite), you MUST (1) call leadbay_report_outreach with verification (gmail_message_id, calendar_event_id, or the user's literal one-sentence confirmation as user_confirmed.ref) AND (2) ask the user about the outcome and set epilogue_status to one of the 4 canonical values: EPILOGUE_INTEREST_VALIDATED_OR_MEETING_PLANED ("Meeting booked"), EPILOGUE_COULD_NOT_REACH_STILL_TRYING ("Trying to reach"), EPILOGUE_NOT_INTERESTED_LOST ("Not interested"), EPILOGUE_STILL_CHASING ("In progress"). Use the user-facing labels in dialogue ("What's the outcome — meeting booked, trying to reach, not interested, or in progress?"); never say "epilogue" out loud. Skipping this step silently de-ranks every future follow-up suggestion because pull_followups depends on honest, current outcomes.
User-facing dialogue:
epilogue_status of type EpilogueStatusType but that's wire-format jargon. The 4 user-facing labels:EPILOGUE_INTEREST_VALIDATED_OR_MEETING_PLANED → Meeting booked 🎯EPILOGUE_COULD_NOT_REACH_STILL_TRYING → Trying to reach ⚡EPILOGUE_NOT_INTERESTED_LOST → Not interested ❄EPILOGUE_STILL_CHASING → In progress 🟢When the user says "not now", "next quarter", "follow up in 3 / 6 / 12 months", "next year", etc., this is a pushback action (not a note). Call leadbay_set_pushback({lead_ids, status}) where status is 3, 6, or 12 (months). The lead drops out of leadbay_pull_followups until the window expires. Use leadbay_remove_pushback to revive a lead ahead of expiry. User-facing dialogue: say "snooze for N months", not "pushback".
When the user mentions a CSV / list / their CRM, use the `leadbay_import_file` prompt — it walks through scan → resolve → preserve → commit. The single-shot tool leadbay_import_leads is for clean, mechanical imports; the prompt handles messy ones.
Roughly the top 10 of every leadbay_pull_leads response carry full AI qualification (qualification_summary.answered > 0, ai_agent_lead_score, ❖ caps in the rendered bar). Leads below the top ~10 are NOT worse — the system is saving resources. A healthy daily rhythm: bulk-qualify the rows WITHOUT ❖ caps so tomorrow's top-10 is richer. Use leadbay_bulk_qualify_leads([leadIds]) for this; default to wait_for_completion:false for any count > 5.
After your first leadbay_pull_leads call, capture response.lens.id (the response nests it under lens) and pass it explicitly as the lensId argument to every subsequent Leadbay call this session. Lens shifts mid-workflow throw away your prior batch — see Rule 1 in the long-running-tools heuristics above.
You don't need to memorize every tool here — each tool's own description carries a RENDERING block (how to present the response) and a NEXT STEPS block (observation → suggestion table). Read the relevant tool's description in full when the user picks an entry point. This overview just gets you to the right starting tool.
After reporting account state, you MAY propose a concrete next step — but only when one is genuinely useful, not by reflex. A reflexive "want me to also…?" on every turn is noise; the user notices and it erodes trust.
Propose a next step when the overview surfaced an obvious unfinished thread or a blocker the user would want resolved — a fresh discovery batch waiting, follow-ups due today, or a quota/auth blocker with a specific unblock action. In those cases the next move is real and worth offering.
Skip it when there's no clear unfinished thread, the user only wanted the status (a bare "where do I stand?"), or the work they asked for is plainly done. A status read that ends cleanly is a complete answer — don't manufacture a next step just to have one.
Read the room. If this user has been dismissing next-step offers, stop making them and let them ask. If they keep acting on them, keep offering. That is your own read of the conversation, not something Leadbay stores for you.
When you do propose, the proposal IS a native choice dialog — never a prose "let me know if…". Route 2–4 mutually-exclusive next moves into your host's next-step widget (ask_user_input_v0 on Claude chat / ChatGPT, AskUserQuestion on Claude cowork / Claude Code). The widget is the question; do not also list the same options as prose.
ALWAYS render NEXT STEPS via your host's next-step widget. Use whichever is in your tool set — the NAME and SCHEMA differ: `ask_user_input_v0` (Claude chat / ChatGPT) takes plain-string options with type:"single_select"; `AskUserQuestion` (Claude cowork / Claude Code) takes object options {label, description} plus a required short header (≤12 chars) and multiSelect, NO type field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have — the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget — the widget IS the question.
If the tool result carries a `next_steps` object, that is the source of truth — use it directly. Each option has a short .label (≤5 words) and a full .description. Map next_steps.options[] into your host widget VERBATIM and in order: for AskUserQuestion (cowork / Claude Code) pass each as {label, description}; for ask_user_input_v0 (Claude chat / ChatGPT, string options only) pass each option's .description as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them — they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO next_steps field.
One exception — skip the widget when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly — the user stated their full plan and does not need a "what next?" prompt.
Pick 2–4 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above — wrong schema fails silently):
ask_user_input_v0: {questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}AskUserQuestion: {questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<≤5 words>",description:"<Suggest 1>"}]}]}User picks → call the matching Calls tool. Constraints: 2–4 mutually-exclusive options, AskUserQuestion labels ≤5 words (full text in description), max 3 questions. Table stays internal; never recite it.
The overview itself returns no next_steps object, so when you DO propose, build the options from this table — pick the 2–4 rows that match what the account state actually showed. If none apply cleanly, propose none (the status read was complete) rather than inventing an option.
All Calls below are agent-callable leadbay_* tools (never an MCP prompt name like leadbay_daily_check_in — the agent cannot invoke a prompt from a turn; route to the underlying tool instead).
| Observation | Suggest | Calls |
|---|---|---|
| Fresh discovery batch waiting / user wants new leads | "See today's best new leads" | leadbay_pull_leads(lensId = pinned) |
| Follow-ups due / known leads to re-engage | "Show follow-ups due now" | leadbay_pull_followups |
| Quota/credit read shows exhausted balance | "Review what's eating your quota" | leadbay_account_status (deeper read) |
| Auth/connection blocker (e.g. 401 / AUTH_EXPIRED on a read) | "Reconnect Leadbay to unblock actions" | (guide the user to re-authenticate — no tool call) |
| Lens audience looks mismatched (batch is off-ICP) | "Adjust the lens audience to match your ICP" | ASK first — collect the target sectors / sizes / exclusions, THEN leadbay_adjust_audience(...) with those params. NEVER call it with no args (an empty call writes the current filter / may clone the default lens — a no-op or unwanted change). |
| Status is healthy and nothing is pending | propose nothing — the overview is a complete answer | — |
GATE — DEFER TO TOOL RENDERING. When you call a Leadbay composite that ships its own RENDERING block (every composite in 0.9.0+ does), render the response using that block's recipe verbatim — score bars, glyph palette, column order, hide-list, link priorities, all of it. Do NOT substitute prose, a numbered list, or a different column structure even when an orchestrating prompt's body suggests alternate framing. Prompt-specific commentary (motivational nudges, summaries, next-action recommendations) belongs ABOVE or BELOW the canonical table, never in place of it.
If the prompt's body and the tool's RENDERING appear to conflict, the tool's RENDERING wins for the structural layout; the prompt's voice wins for the commentary that surrounds it.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.