Every Gamestage game asks a fan before anything records them. The Gamestage client draws the banner for you, stores the answer, and holds every analytics and storage call until the fan says yes. gamestage deploy refuses a game that would record fans without asking.
Quickstart
Name who is asking and link your privacy policy, in gamestage.settings.json beside gamestage.yaml. A deploy writes both into Studio:
{
"consent_brand_name": "Arsenal",
"privacy_policy_url": "https://www.arsenal.com/privacy-policy"
}Then load the client and start the game. The banner appears on its own the first time a fan opens the game:
const game = await gamestage.start({
consentBanner: { settingsIn: document.querySelector("footer") },
});settingsIn puts a "Privacy settings" button in your footer, so a fan can change their mind later. Leave it out and the banner pins a small "Privacy settings" pill in the bottom corner of the screen, outside the page's layout, so a game sized to fit the screen does not gain a scrollbar. Pass null only when your own control calls game.consent.openSettings().
Check it the way a new fan would meet it:
npx gamestage@latest verifyThe consent line passes when a consent control appears before anything is sent, and nothing is sent after the fan says no. The consent-owner line passes when the brand name and the privacy policy link are both set.
What a fan sees
A compact card floating near the foot of the screen, clear of the game's own pill nav and pinned main button: two lines naming who is asking and why, an underlined Privacy policy link, Accept (filled in the game's accent) and Reject (outlined) at the same size side by side, a small Manage preferences link for choosing one category at a time, and a × in the corner. It takes the game's colours and font from its Studio palette when they are readable together. The game stays playable underneath it.
× closes the card without answering. Nothing is recorded, nothing is stored, and the card comes back on the next visit.
The fan is asked once per game, on each device, until the producer changes the privacy policy version in Studio.
| Category | What it allows | On by default |
|---|---|---|
necessary | Playing the game: a fan's session and their round in progress. | Always on |
analytics | Monterosa Analytics, and PostHog or Google if the creator turns them on. | No |
marketing | Offers and news from the game's owner. | No |
functional | Remembering a fan's preferences on the device. | No |
These are the Monterosa SDK's own category names, so an app's answer needs no translating on the way in.
Change the words
Every word on the banner is the producer's, in Studio under Wording. An empty field shows the default.
| Studio setting | Default |
|---|---|
consent_brand_name | None. Required: verify fails and deploy refuses while it is empty. Name your own brand, the one fans know: the banner reads "Arsenal and Monterosa, who run this game…". Only Monterosa's own sample games name Monterosa, and the banner then reads "Monterosa, who runs this game…". |
consent_title | Your privacy |
consent_detail | {brand} would like to record analytics on how you play, so we can improve the game. We only do this if you agree. Data needed to run the game, such as your scores, is processed on the basis of legitimate interests. |
consent_accept_label | Accept |
consent_reject_label | Reject |
consent_choose_label | Manage preferences |
consent_save_label | Save my choices |
consent_analytics_label | Measure how the game is played |
consent_marketing_label | Offers and news from the game's owner |
consent_functional_label | Remember my preferences |
consent_settings_label | Privacy settings |
privacy_policy_url | None. Required, and must be https: verify fails and deploy refuses while it is empty. |
consent_policy_version | 1. Change it when your privacy policy changes, and every fan is asked again. |
Pass consent in from an app that embeds the game
A club app that embeds a game has usually asked its fan already. Pass that answer in and the game never shows its own banner. A host's answer always beats the game's.
With the Monterosa SDK, set it in the host page. It reaches the game on its own:
import { setConsentState } from "@monterosa/sdk-consent-kit";
setConsentState({ necessary: true, analytics: true, marketing: false, functional: true });Without the SDK, post it into the game's frame:
frame.contentWindow.postMessage(
{ type: "gamestage:consent", categories: { analytics: true, marketing: false, functional: true } },
new URL(frame.src).origin,
);Only a message from the frame's own parent is read. A missing category is a no.
Read the answer in your game
game.consent holds the answer. You rarely need it: the client's gates already read it.
| Member | Returns | What it is for |
|---|---|---|
record() | ConsentRecord or null | The full answer: categories, source (host, fan or default), decidedAt, policyVersion, jurisdiction. null while nobody has answered. |
needed() | boolean | True when the game must still ask. |
hostDecides() | boolean | True when the embedding app answered. Hide your own privacy link when it is. |
decide(categories) | boolean | Records the fan's answer. False where the host decides. |
openSettings() | nothing | Opens the choices again. |
onChange(listener) | an unsubscribe function | Called whenever any authority changes the answer. |
Draw your own consent screen
A Gamestage UI game uses the consent component, which reads and writes the same record. Turn the client's banner off so there are not two:
const game = await gamestage.start({ consentBanner: false });Any screen of your own must carry data-gs-component="consent", offer reject as plainly as accept, and call game.consent.decide. verify and deploy check it by loading the game as a new fan, so a page that turns the banner off and asks nobody is refused.
When the check fails
verify or deploy says | What to do |
|---|---|
| The page sent analytics before the fan answered | Send events through game.track, which waits for consent. Remove any tracking script loaded directly in the page. |
| The banner would not say who is asking | Add consent_brand_name and privacy_policy_url to gamestage.settings.json. |
| No consent banner appeared | Load the Gamestage client and leave consentBanner on, or draw the consent component. Check the console for an error that stopped gamestage.start(). |
| The fan said no and the page still sent analytics | Something in the page tracks without the client. Remove it. |
| Consent checked by reading the page, not in a browser | No Chrome on this machine. Install Chrome, or set GAMESTAGE_CHROME, for the full check. |
If your game records nothing about fans at all, set identity.consent.analytics_requires_consent: false in gamestage.yaml and no banner is required. That is your declaration to answer for.
