Recruit Email/Web Activity Tab — Logic Documentation
Shipped: 2026-09-01 · Backlog #704 · PR #1395 (commit 8d288c27) · MGMT portal (bp-mgmt-dashboard)
What it is
Every recruit profile (/growth/recruiting/<id>) now has an Email/Web Activity tab, immediately to the right of Production. It is a single reverse-chronological feed that interleaves:
- Website visits — pages the recruit viewed on bramlettpartners.com (existing Leadpipe data; previously only visible on the recruiting targets list).
- Email sent — every recruiting email that went to the recruit from the Email Campaigns module or as a reply from Inbound Emails.
- Opened email — the recruit loaded the email (tracking pixel).
- Clicked link — which specific link in the email the recruit clicked (click-wrapped URLs).
Rows are grouped by day (Today / Yesterday / date, Central time) with counters for emails, opens, clicks and visits at the top. It uses the same visual pattern as the lead activity timeline.
Why it works regardless of who "sent" the email
Recruiting mail goes out under Eric's identity even when Courtney sends it. Attribution does not use the sender, the From header, or the account: every outbound email is stamped with a unique per-send token. The pixel URL and every wrapped link carry that token, and the token maps back to the recruit. Who pressed Send is irrelevant to the feed.
How tracking works (technical)
- Instrumentation happens at send time in
server/lib/email-tracking/instrument.ts. Only<a href>anchors are rewritten (never bare URLs or text nodes — the Chrome extension's worst bug was breaking links by wrapping across text-node boundaries). A 1×1 pixel is appended before</body>. Unsubscribe links are never wrapped. The stored copy of the email (campaign recipient row / inbound reply record) is the un-instrumented original. - Tracking never blocks a send. If token generation or the tracking write fails, the email still goes out un-instrumented and the failure is logged.
- Public endpoints (no auth, rate-limited):
GET /api/public/e/o/:token— open pixel. Always returns a 42-byte GIF with 200, even for unknown tokens.GET /api/public/e/c/:token/:index— click redirect. 302s only to the absolute http(s) URL stored in the send's link map; anything else is 404. It never redirects to a URL supplied in the request.
- Machine vs. human opens.
classify.tsandurls.tsare verbatim ports of the Chrome extension's logic. Google image-proxy fetches, link scanners and self-origin views are stored and flagged, never dropped; the feed hides them and shows a footnote count ("N automated events hidden"). - Storage (migration
0200_recruit_email_tracking):recruit_email_sends(one row per instrumented send, with the link map),recruit_email_events(opens/clicks with timestamp, link index, target URL, user-agent classification),recruit_email_tracking_settings(kill switch singleton). Colleague merges repointrecruit_email_sendsto the surviving colleague. - Feed API:
GET /api/colleagues/:id/activity-feed— merges Leadpipe web visits with email sent/open/click rows; sorted newest first. - Base URL comes from
PORTAL_BASE_URL(falls back to the request origin). No new env vars.
Kill switch
recruit_email_tracking_settings (single row, enabled boolean). A missing row means ON — production currently has zero rows, so tracking is live. GET /api/recruit-email-tracking/settings reports the state. Turning it off stops instrumenting new sends; already-sent emails keep working and the tab stays visible either way (it still shows website visits and send history).
Deep links
?tab=production · ?tab=activity · ?tab=comments on the recruit page. The legacy agent-profile sub-tab moved from ?tab= to ?sub= to free the parameter.
Known limits / follow-ups (Backlog #706)
- An email opened via Gmail's image proxy within 60s of a SendGrid send is classified as machine (extension heuristic inherited verbatim) — such an open is hidden, not lost.
- Web visits depend on Leadpipe identifying the recruit; anonymous visits don't attribute.
- Deferred hardening (fire-and-forget pixel, per-IP rate limiter, index on campaign recipients) is tracked on #706.
Verifying it
Send a campaign or an Inbound Emails reply to a test recruit, open the email and click a link, then open that recruit's Email/Web Activity tab: expect an "Email sent" row, an "Opened email" row and a "Clicked link" row naming the link target.