What HQ sees on Impact, and what it never sees
Impact is the one page on HQ that sounds, at first glance, like it should need the deepest look into a franchisee's business: it is reporting dollars recovered, decisions acted on, and how much a l…
On this page
The line this article holds
Impact is the one page on HQ that sounds, at first glance, like it should need the deepest look into a franchisee's business: it is reporting dollars recovered, decisions acted on, and how much a location is using Verinode. It would be reasonable to assume that means HQ is reading a franchisee's decisions, documents, and job records directly. It does not. Every number on the Impact page comes from exactly one table, the network data, plus one directory lookup for display names, the network data. Nothing else. HQ's Impact code, and every other piece of HQ-side code, is written against a client called hqAdmin, and hqAdmin has no path to schema the private layer at all, not a slow path, not an admin override, no path. This article explains exactly what that means: what crosses from a franchisee's own account into HQ's network view, what the mechanism is that carries it, and what stays behind on the franchisee's side no matter what.
If you want the full tour of the Impact page itself (the hero, Value Captured, Needs Attention, and Engagement rows), read Impact: the network's return on Verinode first. This article is the trust layer underneath it, the same relationship What HQ sees: the network privacy boundary has to the rest of HQ's product. If you only read one sentence from this whole article, read this one: Impact shows HQ how much value Verinode produced and how actively a franchisee is using it, never the private business records that value was calculated from.
Where Impact lives
Open Impact from the HQ sidebar, under the Intelligence group alongside Benchmarks, Margin & Cash, and Forecasting, at hq.verinode.ai/franchise/impact (also reachable at the shorter /impact alias). Everything described below happens behind that single page: a hero row of network totals, then three tile rows ranking franchisees by realized dollars, by utilization risk, and by IQ engagement.
What Impact actually reads
Two reads, both through hqAdmin, both scoped to schema the network layer:
- the network data. One row per franchisee per network, per rolling 30-day window. The columns Impact's server code selects are:
operator_id,decisions_surfaced,decisions_acted,decisions_parked,decisions_ignored,action_rate_pct,hard_recoverable_cents,soft_conditional_cents,iq_commands,ius_spent,documents_added,surveys_sent,surveys_responded,active_minutes,utilization_grade,window_start, andwindow_end. That is the entire vocabulary Impact has to describe a franchisee. Every tile, every sort, every empty state on the page is built from nothing but those sixteen fields. - the network data. A second, separate read that supplies exactly one thing:
location_name, so the tiles can say "Riverside Restoration" instead of a bare operator ID. Nothing else from the directory feeds Impact.
That is the whole read surface. There is no third query, no fallback path, no "advanced view" that opens a franchisee's underlying account. If a number is not one of those sixteen columns or a display name, it is not on the Impact page, because there is no code path that could have put it there.
Note
Both queries are resilient by design, not just by convention. If the network data has not been created yet, or a read against it errors for any reason, Impact's server code catches that error, logs a warning, and returns the page's empty rollup rather than throwing a 500. A brand-new network, or a network whose nightly rollup has not run yet, gets the same "No data yet" hero pill and per-row empty-state copy described in Impact: the network's return on Verinode, never a broken page.
How a franchisee's numbers get into that table in the first place
the network data does not populate itself, and Impact's own server code never writes to it. A separate process, the nightly HQ aggregate-refresh cron (the nightly refresh cron), is the only bridge between a franchisee's private data and that table, and it is documented and audited as exactly that: a deliberate, one-directional crossing point, not a loophole.
Here is the sequence, for a single franchisee, once a night:
- 1The cron first checks that the franchisee's operator ID actually appears in the network's own the network data. Only franchisees on the roster get rolled up; the cron will not compute or write a row for an operator who is not a recognized member of that network.
- 2For each qualifying franchisee, the cron calls the exact same function the franchisee's own IQ Impact page calls for itself,
getImpactMetrics, over a rolling 30-day window (from 30 days before the run back to the moment the cron fires). This is the same math, not a parallel approximation. It reads decisions, documents, IQ commands, IUs, and survey activity from that franchisee's own private data, the same way it would if the franchisee opened their own Impact tab right now. - 3That read happens inside the cron process itself, using a service-role client with legitimate cross-schema access, the one explicit, documented exception to the rule that HQ-side code never touches schema the private layer. The cron is not HQ-side code in that sense: it is the pipeline that produces core-schema summaries from pii-schema truth, and it is excluded on purpose from the build-time lint that blocks every other HQ file from importing a pii client.
- 4The cron takes the finished numbers, the sixteen fields listed above, and nothing else, and writes (upserts) one row into the network data, keyed on the network and the franchisee together, so each night's run replaces last night's row rather than appending a growing history.
- 5If computing or writing that one franchisee's row fails for any reason, the cron logs the failure and moves to the next franchisee. One franchisee's bad night does not stop the rest of the network's numbers from refreshing, and it does not crash the page. That franchisee's row simply keeps whatever it last had (or stays absent, if it never had one), and the "reporting" pill on the hero reflects that honestly rather than silently padding the count.
Once that row lands, the crossing is finished. From that point forward, HQ's Impact page is reading a summary that was computed and handed off the night before, not reaching backward into the franchisee's live account.
What never crosses
Everything the cron touches to compute those sixteen numbers stays exactly where it was:
- The decisions themselves. Impact knows how many decisions were surfaced, acted on, parked, or ignored, and the percentage that represents. It does not know which decisions, what they said, what dollar figure or carrier or job they were attached to, or who at the franchisee acted on them.
- The dollars behind Recovered and Potential.
hard_recoverable_centsandsoft_conditional_centsare totals. There is no itemized list of the supplements, settlements, or line items that sum to those totals, and no drill-down from an Impact tile into them. - IQ commands, as content.
iq_commandsis a count of operator-initiated conversations with IQ. HQ never sees what was asked, what IQ answered, or any transcript. Same forius_spent: a spend total, not a ledger of what was spent on. - Documents.
documents_addedis a count. The documents themselves, what they are, what they contain, live entirely inside the franchisee's own vault and are never read by this pipeline for anything beyond a count. - Surveys, as answers.
surveys_sentandsurveys_respondedare counts of activity. Question text and response content stay on the franchisee's side. - Active minutes, similarly, is a single number from session heartbeats, not a log of when or what a franchisee's team was doing inside IQ.
None of that is a policy choice that a future engineer could quietly relax. The getImpactMetrics call that produces these numbers runs once, inside the cron, and only its sixteen-field output ever leaves that process. There is no second function, no export, no admin panel that reaches further.
Where the franchisee's name comes from
The one piece of identifying detail anywhere on Impact is the franchisee's display name, and it does not come from the same place as the dollars. It comes from location_name on the network data, a row your network's own aggregator cron maintains separately (it does have legitimate access to read an operator's business name, the same way it reads other roster fields for the rest of HQ's product). Impact's read side only ever looks up a name that has already been resolved into that directory table for a franchisee who belongs to your own network. It has no ability to look up a name for anyone outside your roster, because there is no query path to any table where that lookup would happen.
The enforcement, not just the intent
This boundary is not a coding convention that depends on every engineer remembering it. It holds at four separate layers, and Impact is not an exception to any of them:
- The client. Every file under HQ's franchise code, including the product, imports
hqAdminand nothing else.hqAdminis bound to a database connection whose role has no grant on schema the private layer, so even a mistaken query against the private layer from HQ-side code would be refused by the database itself, not merely blocked by application logic. - A build-time check. An automated lint scans every file under HQ's scopes (
lib/franchise/**,app/(franchise)/**, and the equivalent HQ paths) and fails the build if any of them import a pii-schema client or write a raw.schema("pii")query. The nightly aggregate-refresh cron is the one explicitly documented exception, because its entire job is to be the sanctioned bridge, not a leak. - The write path. Only the cron writes to the network data. Impact's own page code is read-only against that table; there is no save, edit, or annotate action on the Impact page that could turn a read surface into a write path back toward pii data.
- An audit trail. Cross-schema access from the cron, and any mutation on the core side, is logged, so the one legitimate bridge point is itself observable, not a quiet backdoor.
Why this is the design, not a limitation
A franchisee agreeing to run Verinode inside their business is trusting that their day-to-day records, an adjuster email, a supplement amount, a specific job, stay theirs. Impact exists so a franchisor or PE-backed multi-location leadership team can still answer "is Verinode working, and for whom" without that trust being the price of the answer. The page tells you how much value showed up, network-wide and per franchisee, and whether a location is engaging with the tool or falling quiet. It does not, and structurally cannot, become a window into why a specific decision happened or what a specific document said. Franchisees own their data. What flows up to HQ on Impact is a summary their own account already produced for itself, computed the same way whether or not HQ ever looks at it, and nothing more.
Related articles
- Impact: the network's return on Verinode, the full page tour: the hero, Value Captured, Needs Attention, and Engagement.
- The network impact hero, a close read of the headline, the pill, and the three secondary dollar and rate tiles.
- What HQ sees: the network privacy boundary, the platform-wide version of this same discipline, covering Benchmarks, Network Health, and the industry reference line.
- HQ overview, how Impact fits alongside Benchmarks, Margin & Cash, and Forecasting.
- Hard Recoverable vs Soft Conditional dollars, why Recovered and Potential are never added into one figure.
Data sources
- 1.HQ Franchise Portal Specification. Verinode.
- 2.Verinode Data Use Policy. Verinode.