Consents
Profile consents are checkboxes whose acceptance is recorded against the user's Fans United profile. Eight components accept them today — Predictor, TopX, ClassicQuizPlay, PollVote, EitherOrPlay, EventGamePlay, MatchQuizPlay and PersonalityQuizPlay — all through the same ConsentDef shape. CollectLead has its own, older LeadConsent shape (id / label); both are accepted wherever consents are read.
ConsentDef
interface ConsentDef {
consentId: string;
body: string;
required?: boolean;
defaultChecked?: boolean;
position?: "before" | "after";
}| Field | Type | Default | Description |
|---|---|---|---|
consentId | string | — | Consent identifier. Sent as-is to the Profile API and used to detect an already-accepted consent. |
body | string | — | The checkbox label. Supports HTML and the {{privacyPolicyUrl}} / {{termsAndConditionsUrl}} tokens. |
required | boolean | false | Blocks the gate's CTA until ticked. An inline error appears if the user tries to continue without it. |
defaultChecked | boolean | false | Pre-ticks the checkbox. Ignored for required consents — a required consent must be an explicit action. |
position | "before" | "after" | "before" | Game widgets only — every *Play / PollVote component. Where the checkbox is shown; it is per consent, so one array can drive both surfaces. Ignored when a lead form is the gate — see below. |
URL tokens
{{privacyPolicyUrl}} and {{termsAndConditionsUrl}} are replaced with links to the entity's own branding URLs — branding.urls.privacyPolicyUrl and branding.urls.termsAndConditionsUrl, configured on the entity in the back office. The link text comes from the widget language (Privacy Policy / Terms and Conditions), or from labels.privacyPolicyUrlLabel / labels.termsAndConditionsUrlLabel where a component exposes them.
If a token has no URL behind it, the link text is rendered as plain text and a warning is logged — the consent is never hidden because of a missing URL.
const consents: ConsentDef[] = [
{
consentId: "terms",
body: "I accept the {{privacyPolicyUrl}} and the {{termsAndConditionsUrl}}",
required: true,
},
{
consentId: "marketing",
body: "Send me news and offers by email",
defaultChecked: true,
},
];Game widget surfaces
The game widgets take consents: { items, labels? } and render each consent exactly once, on the surface that owns it. The table below uses the quiz's wording; the others behave the same, with their own action in place of the participation — a vote, a prediction or a game:
| Quiz configuration | Consent surface |
|---|---|
authRequirement: "LEAD" + leads.position: "before" | The lead form shown before the questions — all consents, position ignored |
authRequirement: "LEAD" + leads.position: "after" | The lead form shown after the last answer — all consents, position ignored |
| No lead form | position: "before" → the start screen; position: "after" → its own step before the participation is submitted |
The leads configuration wins because the lead form is already the quiz's gate — adding a second gate screen next to it would ask the user to click through twice.
Where the "before" gate lives differs slightly per widget, because each reuses a screen it already has:
| Component | "before" surface | "after" surface |
|---|---|---|
ClassicQuizPlay | The start screen, forced to appear for every quiz type | Its own step before the participation submit |
PersonalityQuizPlay | A gate screen before the questions | Its own step before the participation submit |
PollVote | A gate screen before the poll | Its own step before the vote |
MatchQuizPlay | A gate screen before the prediction UI | Its own step before the prediction |
EventGamePlay | A gate screen before the prediction UI | Its own step before the prediction |
EitherOrPlay | The tutorial screen, whose Start button records them | Its own step before the results |
Every gate keeps its template's participation layout, and its CTA defaults to that widget's own button label.
Without a lead form:
"before"— the start screen is shown for every quiz type, including the ones that previously dropped the user straight into question 1. The consents sit above the Start button; the ticked ones are recorded when the user starts."after"— pressing Finish stops the timer, then a consent step appears before the participation is submitted. Because the timer is already stopped, reading the consents never inflates atimedquiz's score, and acountdownquiz cannot auto-submit behind the step.
Labels
interface ConsentsLabels {
title?: string;
description?: string;
ctaLabel?: string;
requiredError?: string;
}One set, used by both standalone surfaces — the start screen and the consent step. Each field falls back to the widget language: the start screen's button defaults to the game's start label, and the consent step's to its finish label, since that step is what submits the participation. ctaLabel overrides whichever button is shown; requiredError is the message shown when the user tries to continue with a required consent unticked. Inside a lead form only requiredError applies, since the form supplies its own title and description.
How many checkboxes appear
Branding URLs are link targets, not checkboxes. The only checkbox they produce on their own is the lead form's built-in agreement line — "I agree to the Privacy Policy and the Terms and Conditions" — and that appears only when no consents are configured:
| Surface | Branding URLs, no consents | Branding URLs + N consents |
|---|---|---|
| Lead form | 1 built-in agreement checkbox | N checkboxes — the built-in one is replaced |
| Start screen / consent step | none | N checkboxes |
To keep an agreement gate while collecting other consents, express it as a required consent whose body uses the URL tokens. That is also the stronger option: a configured consent is recorded through the Profile API, while the built-in checkbox is not stored anywhere.
Already-accepted consents
Accepted consents are loaded once when the widget mounts and filtered out, so a returning user is never asked twice. If that lookup fails, nothing is filtered and the consents are shown — the widget errs toward asking again rather than silently skipping a consent.
A failed consent submission is logged and swallowed: the user still plays and the participation is still submitted.
Related
- Lead Collection — the lead form that absorbs a quiz's consents.
CollectLead— standalone lead form with its ownconsentsprop.
Updated about 2 hours ago
