← All posts

Discovery detection behind a privacy gate

Vocion now detects which of your recorded calls were discovery calls — without ever letting an agent read a transcript that didn't first match a CRM record you own. Plus: automations become inspectable, testable, and owned.

Vocion Teamvocion-v2.14.0

Once Zoom and Granola are connected, recordings pile up fast: prospect intros, all-hands, 1

, candidate interviews, board calls. Somewhere in that pile are the discovery calls that should be turning into proposals — but finding them by having a model read every transcript wholesale is a privacy problem, not a feature. Your internal meetings are none of the sales agent's business. Releases v2.5.0 through v2.14.0 (2026-08-18 to 2026-08-20) ship discovery-call detection built the other way around: match first on metadata, read only after a match, and never hand the transcript to the agent at all.

What's new

Why we built it

The privacy guarantee is structural, not a prompt. We didn't write "please don't read internal meetings" into a system prompt and hope; we built a funnel where the unwanted state is unreachable. Stage one sees only metadata — titles, hosts, attendee emails. Only a meeting that matched an owned CRM record gets a candidate row, and only a candidate row opens the content gate. When detection moved from a deterministic job to an agent-driven loop in v2.8.0, that guarantee had to survive an agent choosing its own steps — so classify_call collapses read, classify, and record into a single server-side function. The transcript never enters agent-steered context, and an assessed-but-unlogged call is not a reachable state. Refusals are typed (no_candidate, no_transcript), and the tools exist only for agents explicitly granted them.

Fail-closed is the right default, but silent fail-closed is a bug. Zoom↔calendar correlation is id-based, never time-based, so a recording with no attendee metadata matches nothing — by design. The trouble: on 2026-08-20 a real 31-minute prospect intro sat among 318 scanned recordings, matched nothing, and reconcile reported zero gaps. Match and coverage were computed from the same blind metadata, so the system was confidently wrong. Now every fail-closed meeting is named in an unmatchable list (metadata only) with the reason and the actual fix — and name-in-title matching rescues the common case outright. The same instinct drove v2.7.0: the sweep first shipped scheduled but unobservable, where a run that scanned nothing, ran clean, or threw were indistinguishable afterwards. Run rows, owners, and dry-run replays fixed that.

Where to find it

The ledger lives at Dashboard → Discovery; the sweep's automation card (with Test run) at Dashboard → Team → Automation. Grant the tools to an agent via harness.grantTools, schedule the check with a do.prompt automation, and see Automations and Tools for authoring. Upgrading spans migrations 0049–0053. Full detail in PRs #55, #56, #57, and #59 on vocion-core.