Age-gating and compliance done right
3 min read
You want to ship a loot box, or open chat, or player-authored levels. Then you remember: kids play games too, and the moment a minor can open a paid loot box or talk to a stranger, you're standing in the middle of COPPA, GDPR-K, and a dozen national age-rating regimes. Suddenly you're reading legal blogs instead of building your game.
You are not going to become a lawyer for this. The rules are knowable, they differ by feature and jurisdiction, and they change. That's exactly the kind of thing a backend should handle for you, so Triggair ships age brackets and per-feature gates as a first-class primitive, and gives you a single map to read on the client.
Ask once, store nothing
You establish the player's age bracket once. You can hand Triggair a bracket directly, or a birth year. If you pass a birth year, it's mapped to a bracket and immediately discarded. No date of birth is ever stored. That's privacy by design: you can't leak a DOB you never kept.
// From a neutral age screen: never ask "are you 18?", ask the year.
await tg.compliance.setAge({ birthYear: 2014 });
// or, if your platform already knows:
await tg.compliance.setAge({ bracket: 'adult' });
const { bracket, jurisdiction, gated } = await tg.compliance.status();
The gated object is the whole point. It's a server-computed map of which sensitive features are allowed for this player in their jurisdiction, and you don't encode any rules yourself.
Gate features off the status map
Read gated on boot and let it drive your UI. Each key is a real compliance surface: lootbox, real_money_iap, open_chat, public_ugc, behavioral_push, friend_from_stranger.
const { gated } = await tg.compliance.status();
if (!gated.lootbox) {
hideLootBoxStore(); // show direct purchases instead
}
if (!gated.open_chat) {
useCannedPhrasesOnly(); // quick-chat wheel, no free text
}
if (!gated.public_ugc) {
hideCommunityBrowse(); // play your own levels, don't publish
}
These gates fail closed. If age is unknown, every sensitive feature reads false: a player who hasn't set an age gets the safest possible experience, automatically. You never have to remember to handle the "we don't know yet" case, because the default already handles it.
If you want to show players why something is unavailable, tg.compliance.policy() returns the human-readable policy behind the current gating.
The neutral age screen
The one piece of UI you own is the age prompt. Keep it neutral: ask for a birth year, not a yes/no "are you an adult?" A binary question invites lying and doesn't satisfy neutral-age-gate guidance. A simple year dropdown, no cartoon mascot nudging toward "adult," and you're done.
async function onAgeSubmit(year: number) {
await tg.compliance.setAge({ birthYear: year });
await bootGameWithGates(); // re-read status, build the UI
}
Gotchas
- Read
gated, don't compute it. The temptation is to writeif (age >= 18)yourself. Don't. The same age is treated differently in different jurisdictions, and Triggair already resolved that against the player's location. Your job is to honor the map. - Re-check after
setAge. The gates change the instant you set an age, so re-readstatus()before building the UI rather than caching a boot-time snapshot from before the player answered. - Unknown age is a real state. Don't block the whole game waiting for an age. Let the fail-closed defaults run the safe experience, and unlock features as the player provides age.
- Every compliance error is typed with a
code, a humanmessage, and anagent_hint, so your AI coding assistant can self-correct if it calls something wrong.
Establish a bracket once, never touch a DOB, and let the gated map decide: that's compliant loot boxes and chat without a single line of legal logic in your codebase.