A visibility-aware timer on the estimate page that accumulates only the seconds the tab is actually visible, flushing every 30 seconds and guaranteeing a final flush on unload via sendBeacon. The server stamps first-open on the link, logs an estimate-opened notification once, and accumulates total time on the viewer row.
Also called: did they open my quote · how long did they look at it · estimate opened alert
You hear about it the first time they open the quote
Following up was a guess: call the same day and you are nagging, call Friday and somebody else has been out to the site. The first open stamps the estimate and fires one alert to whichever roles you route it to, in the bell and by email. It fires once for the deal rather than once per page load, so a buyer who opens it four times on a Sunday night does not produce four alerts on a Monday morning.
Read time, because ten seconds and twelve minutes are different calls
The quote counts only the seconds it is actually the tab in front of somebody, and stops the moment they switch away. A glance and a real review arrive as two different numbers instead of one identical open event. Totals are capped at a day, so a tab left open on a laptop overnight cannot report as twenty-four hours of interest.
It tells you who opened it, not just that somebody did
Time is recorded against the viewer who identified themselves on the link, so a quote forwarded to a spouse, a business partner or a lender shows up as a second reader rather than as more minutes on the first. A buyer who sends your document on has moved, and that is worth knowing before you pick up the phone.
Checking your own quote never trips the alert
A builder opening their own customer link used to mark the estimate as read, so the one signal worth acting on was the one you could not trust. Internal visits are excluded where the page records and again where the server writes — shutting one door and not the other changed nothing, because the heartbeat tripped the alert a few seconds later anyway. The check runs against the real sign-in rather than anything the browser claims, so nobody opts out of being counted by editing a request.
The minutes right before they call you are not the ones you lose
Read time is sent every thirty seconds and once more as the tab closes, using the browser's send-on-exit path so the final stretch still arrives. That last run through the payment schedule before somebody phones is the most informative part of the whole session, and a timer that only reports on a clean page load misses exactly that.
- 1Time accrues only while document.visibilityState is visible
- 2A 30s heartbeat POSTs to /api/client-portal/track; unload uses navigator.sendBeacon
- 3The route accepts both JSON and beacon text bodies and clamps the reported seconds to 24 hours
- 4First open stamps opened_at and fires a once-only builder notification
- 5Internal platform visits are excluded, verified against the real session
Knowing a quote has been opened four times is the difference between calling with a reason and calling to nag. For a stretch the tracking never ran at all — it read the wrong storage under the wrong key, so it exited immediately and no read-time was ever recorded, and builders were following up blind while believing they had data. Builder previews are excluded on both the page and the recording endpoint, since fixing one door and not the other fixes nothing, and the check runs against the real platform session because trusting the browser would let a customer opt out of tracking by editing a request.
- No signal on whether a quote was read
- Follow-up timing based on guesswork
- Builder previews firing false open alerts
See it on your own jobs
Twenty minutes, your numbers, no slide deck. We’ll build one of your real buildings in front of you and send you the estimate link at the end — yours to keep either way.
or keep browsing features →