Everything LocalPulse does,
by the time someone hits “contact us.”
Built for the question agencies actually ask — “did anyone from our target city just land on the contact page?” Skip past the funnels and cohort builders. Here’s the whole product, surface by surface.
Install in under a minute. See visits in seconds.
One <script> tag per client. The snippet uses sendBeacon first and falls back to fetch + keepalive — so a visitor closing the tab on the way to the contact form doesn’t lose the visit.
- Per-client snippet key. Each site gets its own data-client identifier; no shared pixels, no cross-tenant leakage.
- Real IPs through proxies. Honors CF-Connecting-IP, True-Client-IP, X-Real-IP, X-Forwarded-For in that order, so Cloudflare in front doesn’t mask the visitor.
- Dynamic CORS. Access-Control-Allow-Origin echoes the request Origin so credentialed sendBeacon requests don’t bounce.
- Session-window grouping. A 30-minute session window extends existing rows instead of inflating the count, and Slack only pings on first detection.
<!-- paste before </body> --> <script src="https://lp.app/s/9f2a.js" data-client="castle-rock-fitness" defer ></script>
Geo as a primary signal, not a filter.
Every visit is classified against your client’s configured target cities, with region as a fallback. Multi-location clients tag each visit with the specific branch it matched.
- City beats region. Perfect city match wins; region is the fallback if no city matches across the location list.
- matchedLocationId on every row. Stored on the Visit, so the dashboard can break Local sessions down by branch. The bundled scripts/backfill-matched-location.ts re-tags historical visits when you add a new target.
- ISP buckets. business / isp / hosting / education / government — so a Comcast subscriber doesn’t read like a lead and an AWS bot doesn’t inflate the feed.
- VPN-aware. iCloud Private Relay and ~20 named VPNs are flagged with a shield badge instead of being silently mis-attributed.
One row per session. The whole story inside it.
Click any row. It expands inline into the full session — page journey with dwell, attribution, device, locale, ISP, identity, raw user agent. No second page, no modal.
- Filter tabs that mirror the question. All / Businesses / Local / Identified / Today / This week. Per-location filter for multi-location clients scopes both feed and analytics.
- Per-row badges. Local (pin), Returning (loop count), Identified (user check), VPN / Private Relay (shield) — with an explainer tooltip for the last.
- Honest pagination. Past 10,000 visits the UI shows “10,000+” instead of walking the table — saves the database and your patience.
Get a name next to the visit. No CRM hookup.
The snippet itself watches form submissions on the host site, scores each input against HTML5 type, autocomplete, and a hint string built from name + id + placeholder + aria-label + label text, then upserts identity on (clientId, visitorId).
- Sensitive fields refused. Password, CVV, SSN, tokens and similar are skipped unconditionally — no override, not even for the agency.
- Hint matching with priority order. Word-boundary regexes, scored — so 'first_name', 'fname', 'given-name', and a label that reads 'first name' all resolve to the same field.
- Per-client scope. off / local / all. Most agencies set this to local for noise control; B2B teams flip it to all.
- Upsert on (clientId, visitorId). Same visitor coming back through a different form updates the identity rather than duplicating it.
- password — sensitive type
- message — no identity hint
An alert that earns the ping.
Per-client webhook with a four-mode scope — off / local-only / all / b2b — and a block-kit payload with the six things you actually need: location, company, device, dwell, source, keyword.
- First-visit semantics. Slack fires on first detection of a session, not on every page view. A returning visitor extends the row instead.
- Test-fire from settings. One button in client settings pushes a sample alert to the configured webhook so you can preview the format and channel.
- Page journey + deep link. Block-kit payload ends with a “View in LocalPulse” button that opens the visit detail directly.
- /· 0:38
- /services/water-heater-repair· 2:11
- /about· 0:54
- /contact· 2:58
A weekly read that doesn’t need explaining.
Designed HTML digest — daily, weekly, or monthly — with a Local highlights section that names the matched branch for multi-location clients. Everything runs on the client’s timezone, not yours.
- Per-client schedule. Cadence, hour-of-day, day-of-week (weekly) and day-of-month (monthly) — every value honors the client’s IANA timezone.
- Idempotent delivery. Driven by /api/cron/digest. Re-running inside the same period is a no-op; missed periods get caught up on the next tick.
- Designed, not auto-generated. Header, headline numbers, local highlights, and a list of the named visits the client should care about — no spreadsheet attachments.
Charts shaped like the question.
Visits over time with a Local overlay. Top campaigns with provenance. Local-by-location breakdown for multi-location clients. Traffic sources, devices, top cities, hour-of-week heatmap of local sessions.
- Server-side aggregation. Every aggregation runs in Postgres against indexed columns — (clientId, createdAt) and (clientId, isLocal).
- Rollup-backed long ranges. 90d and all-time read pre-aggregated VisitDailyStat rows, so cost is O(days), not O(visits).
- Local cities highlighted. Top-cities bar tints the matched cities so the client doesn’t have to read labels to find the leads.
Quiet pipes. Visible health.
LocalPulse runs as a managed service with rollup-backed analytics for the long ranges and a background install verifier checking every client site every six hours.
- Rollup-backed analytics. 90-day and all-time views read pre-aggregated VisitDailyStat rows so cost is O(days), not O(visits). Dashboards stay fast at every site count.
- Install verification. Every 6h, with a Chrome user-agent to bypass naive bot blocks. The status pill overrides to “active” whenever real visits land — so a Cloudflare block doesn’t read as “broken”.
- Cloudflare-friendly tracking. Real visitor IP via CF-Connecting-IP, True-Client-IP, X-Real-IP and X-Forwarded-For — in that order — so a proxy in front doesn’t mask the visit.
- Retention you can read. Raw visits roll off after 365 days, after the daily rollup has captured them. Aggregate rollups outlive the raw rows for long-range analytics.
- Track APISnippet ingestionokreal-time
- DashboardApp + authokalways-on
- Slack deliveryOutbound webhooksokfirst-visit fires
- Install verifierBackground snippet checkokevery 6h
- Email digestsDaily / weekly / monthlyokon schedule
One snippet. One Slack channel. Real leads.
We’re running a closed pilot while the dashboard finishes hardening. Reach out and we’ll add you to the early-access list.