Consent management across multiple websites: one notice, changed once
Consent management across multiple websites, done once: change the visitor notice for your organization and every site running the embed follows within minutes, while a scheduled check reads the sites you list for services nobody declared.
Updated
The short version
- Change the visitor notice once for the organization, and every site running the embed picks it up within minutes, with no release on any of them.
- Each visitor gets whichever is stricter: the site's own setting, or the rule where they are.
- Once a day, a check reads a few pages of each listed site for services nobody declared, and a person on your team decides what each one is.
- The check reads markup and headers and doesn't run the page, so a tag a tag manager adds at runtime is governed once you declare it.
You change the notice once, and every site picks it up
It's Friday afternoon. Legal wants one sentence in the cookie notice reworded, and you run a product site, a docs site and a blog. Each grew its own banner component in its own repository, the wording drifted somewhere around the second one, and a one-sentence change is now three pull requests, three deploys and a blog nobody remembers until next quarter.
With Runner the notice isn't a component in any of those repositories. Each site carries the same one line of script, and the notice that line draws is assembled from your organization's policy when the file is served. Change the wording, the look, or whether the notice asks before collecting — once, in the organization's settings. Every site running the embed picks it up within minutes. Nobody ships a release.
The file is cached for five minutes rather than baked into anyone's build. Its major version is pinned in the address, so a policy change reaches every site while a breaking change to the script ships beside the old version, never on top of it. A wrong or withdrawn key gets back a file that quietly does nothing, never an error on your page.
Picture this
Northwind runs three sites on one Runner organization. Someone rewords the notice's second sentence in organization settings and goes to make a coffee. By the time it's cool enough to drink, the product site, the docs and the blog all show the new sentence, and not one of their repositories has a new commit.
Each site keeps what's genuinely its own
Most of a notice is policy, and policy belongs to the organization. Some of it is a fact about one site: the languages its pages are published in, the privacy page Learn more should open, how the notice should look sitting on that product. Those are set per site. So is the list of services each site runs — its analytics, its ad tags, its newsletter pop-up — because the notice asks a site's visitors about the categories that site declares, and a docs site rarely runs what the product site does.
A site can also part ways with the organization on whether it asks and how much it collects. When it does, that site's settings page marks it Overridden, names the fields, and says plainly that a later organization change won't reach them. A departure is always visible.
Some settings never vary by site. The switch that turns collection off is one, along with how long data is kept, the privacy regime the organization declares and the domains it has registered. Turn that switch off and every site stops collecting at once — including a tab somebody left open since this morning, because the same check runs where events arrive, not only in the script.
Every visitor gets the stricter of your setting and their region's rule
You don't keep a notice per region per site. You set each site to ask before collecting or to collect and inform, and each visitor is shown whichever is stricter: that setting, or the rule where they are. A site set to inform still offers Accept and Decline to a visitor whose region requires opt-in first. A region nobody has written a rule for gets the strict answer.
That answer is worked out for each visitor as the page loads, from where the platform's edge says the request came from, and it's never cached — not in the browser, not at the edge — so one visitor's region is never handed to the next. The notice file itself stays shared and cached. If the answer is slow or never arrives, the notice asks.
Where a visitor's region doesn't require asking first, a site set to inform starts analytics and marketing switched on — only those of the two the site declares — and the preference centre, opened from a link on your site, is where the visitor turns them off. They stay off from the start whenever the visitor already made a decision, in either direction; whenever their browser sends a Global Privacy Control signal, which withholds the default every time; and on any site set to ask, which turns nothing on by default for anyone.
The short version
Ask or inform is your call, per site. A visitor's region can only make it stricter, and a region nobody has written a rule for asks.
A scheduled check reads the sites you list, so nobody has to remember what got added
A declared list is kept by hand, which makes it exactly as current as the last person who touched it. So on a daily sweep Runner reads a few pages of the sites you've listed with a live tracking key, the longest-unchecked first, and compares what they name with what that site declares. Shipped something just now? Press Check the site now.
It's a polite visitor. It fetches pages only on the address you registered for that site, over https, one page at a time rather than in a burst, and it identifies itself as Runner's consent scanner. It never follows a link or a redirect somewhere else. From each page it takes the third-party addresses the HTML names and the names of the cookies the server sets — the names, never the values, because a session cookie's value is a live credential and nothing in the check reads one. Your own subdomains count as you: a stylesheet from static.example.com is not a third party on www.example.com.
Here's the edge of it. The check reads the page source and doesn't run the page, so anything a script adds after loading — most of what a tag manager exists to do — is invisible to it. A tag loaded that way is governed once you declare it, and a site you haven't listed isn't checked at all. The review screen says so above its results, because finding nothing isn't the same as there being nothing.
Your team decides what every finding is
The check proposes; it never files. When it recognises a service, it suggests the category that service usually belongs in. An address it doesn't recognise gets no suggestion at all, because a confident wrong category would tell your visitors something false about what they agreed to. Nothing reaches the notice until someone on your team picks a category and confirms it, and from then on that site's visitors are asked about it.
Setting a finding aside says it needs no declaration. It doesn't stop anything running, and the product says exactly that when you click. Either way the answer sticks: the next check won't re-ask about something you already confirmed or set aside. A service you declared that the check never saw shows up as information, not a fault, since it may well be loading from inside a tag manager.
When a check turns up something new, your team gets an in-app notification with a count and a link to the queue — never the vendor's name, because the queue is where a finding gets its evidence and its decision. Email is there too, off until you turn it on. And if a site doesn't answer, the check records that it couldn't read the site and writes no findings, so an empty queue is never what a failed check looks like.
Picture this
Tuesday: someone on the blog team pastes a newsletter pop-up's script tag straight into the blog template. Wednesday's check finds an address the blog never declared and queues it with the pages it turned up on. The notification says one new thing was found, not what. You open the queue, pick a category for the pop-up, and the blog's visitors are asked about it from then on.
One page shows who asks the visitor, on every site
Settings has a page called Who asks the visitor. It lists every product in the workspace with how its notice is set, whether it uses the shared wording or its own, the languages it's in, and the services the visitor's answer governs there.
It puts the awkward row first. A site that collects while Runner draws no notice sits at the top, because that's correct when the site draws its own banner and gates the embed on the answer — and it looks identical when nobody asks at all. So the page says what Runner knows, that it draws no notice there, and asks you to confirm the rest rather than guessing about a codebase it has no view into.
A site with no live key is listed as not collecting, not as missing a notice. A site whose settings couldn't be read is named rather than dropped, and never painted as good news or bad.
Your own scripts ask the same question, and every answer is kept
The embed hands the visitor's answer to your page, so the scripts each site runs can gate on one decision instead of a banner of their own. The consent install snippet puts a small inline stub ahead of the script, so window.runnerConsent answers from the visitor's stored decision before the embed has finished downloading, and subscribe() calls you back the moment the full answer is ready.
Where a site runs Google's tags, the embed passes each decision on as a Consent Mode update. Each site still sets its own consent-starts-as-no default in its HTML, ahead of Google's loader, and that order is the whole trick.
Every choice is recorded against the site it was made on: which categories, when, how it arrived — the notice, the preference centre, or your own page's code acting for the visitor — how the site's notice was set, which copy variant was on screen, and the embed's version. The record holds no visitor identifier. It answers what was agreed, when and under which wording, and none of that needs to know who. A no is recorded exactly like a yes.
A preference centre, which your site opens from any link with one call, lets a visitor change their mind later. Where a site offers Recognition — knowing a returning visitor as someone the site already knows — it's switched on only there, by the visitor, and never by Accept.
Try it
On a site running the embed, answer the notice, open the browser console and type runnerConsent.get(). You'll see each category as true or false, when you decided, and a recordId. That id is minted in your browser for that one decision, and deciding again mints a new one, so it names the choice and never you.
One system for the whole path
The mechanism above is one of the jobs Runner does on one record, under one login. Access is by application.





