One URL per lead
v2.26.0 gives every lead a dedicated page at /gtm/lead/{hubspot_id} — the decidable card, the research record, and the decision trail on one addressable URL.
The personalization queue could tell you a lead existed and let you decide it inline, but it
could not give you a place. Expanding a row built the dossier in the list; collapsing it threw
that away; nothing you were looking at had a URL you could send to someone. v2.26.0 gives each
lead its own page at /gtm/lead/{hubspot_id}, and turns the queue back into what a queue should
be — a list.
What's new
- A page per lead.
/gtm/lead/{hubspot_id}, looked up by thecontacts:{id}ref the brief already carries. Addressable, linkable, reloadable. - The queue is a pure list again. The chevron, the inline card, the inline dossier and the selection checkboxes are gone. Every row links to its lead's page. Lanes, counts, search and sort stay exactly as they were — and the server now ships the list only the row line, not the whole dossier behind every row.
View Researchlands somewhere. The review card's link opens the lead page, from both deciding surfaces.- The card resolves server-side. The page looks up the back-linked
action_rununder the same pending predicate the review feed applies and builds the card with the action's own presenter — so the card needs no client fetch and cannot flash on load.
Why we built it
A review queue answers "what needs me next." It is the wrong shape for "tell me everything about this lead." Those two questions were sharing one surface, and the dossier lost every time: it only existed while a row was expanded, it re-fetched on every toggle, and it could not be linked to. Meanwhile the queue paid for it — every row shipped a full dossier the reader would probably never open.
Splitting them lets each be honest. The queue lists and routes. The lead page holds the record.
How to use it
Open the queue at /gtm/personalization and click any row. You land on that lead's page, and what
the top zone shows depends on where the lead actually is:
| Lead state | Top zone |
|---|---|
Pending personalization.enroll run | The same decidable card the review queue shows — Decline / Snooze / Enroll |
| Decided | The record of the decision: enrolled, held, sent or snoozed, with the sends read-only |
| Pending but snoozed | Renders when the snooze returns |
| Approved but failed in execution | Named as failed, rather than read as still waiting |
| An id the sweep never queued | An empty state, with a door into HubSpot when the portal id resolves |
Deciding the card here decides the same run the review queue would have decided — one operation, one run, whichever surface you were standing on.
Below the decision zone sits the research record: the brief, the reference articles derived from the claims' URL sources, the claims themselves, what research could not reach, the confidence, and the timeline.
To exercise all of it locally, the seed now stages the states rather than just the happy path:
cd vocion-core/packages/core
npm run seed:personalization-queue
That stages a decided lead carrying its approved sends, a brief-failed lead and a draft-failed lead, and claim sources are now real URLs so the reference-articles rail actually renders.
What's next
- The
imagecontent kind for review cards, so visual-QC objects present through the same card template as email and document. - Coverage for the personalization phase 1 releases (
v2.17.0–v2.19.0), still unblogged.
Links
- GitHub release: https://github.com/vocion/vocion-core/releases/tag/v2.26.0
- Dashboard surface:
/gtm/lead/{hubspot_id},/gtm/personalization - Code:
packages/core/src/features/personalization/LeadDetail.tsx,LeadContext.tsx - Migration:
packages/core/migrations/0065_lead_brief_drafting.sql