Age Gate is in Beta. It is built in the API and the Console. The age fields are not yet in the TypeScript SDK types or the widget’s events, so you read them from the API directly, as shown on this page.
What it answers, and what it never reveals
The age result is positive-only. There are two outcomes:
An Age Gate never returns a date of birth, an age, an age range, or an “underage” answer. BotShield tells you that a human is over your threshold, never who they are or how old they are. You receive no personal data.
Where the age signal comes from
BotShield does not estimate age, scan documents or analyze faces. During the confirmation on the user’s phone, the BotShield app asks the phone’s platform for the age range that the platform already offers to apps:- On iPhone, Apple’s Declared Age Range.
- On Android, Google Play Age Signals.
age_source tells you which platform signal was used: apple_declared_age_range or play_age_signals.
The result is as good as the platform’s signal. How the platform established the age range is the platform’s own process. Check whether that meets the rules that apply to your product.
Place an Age Gate
In the Console, select BotShield Gate → Place a gate, choose Age as the gate type, and pick 13+, 18+ or 21+ under Verify over. See Place a gate for the full walkthrough.- An Age Gate always runs in Live mode. The age signal is read on the phone during the confirmation, so there is no instant pass for returning users.
- You can change the threshold later in the gate’s Configuration card. Each request records the threshold that applied when it was created.
- The widget button looks the same as on a Human Gate, and there is no age-specific attribute. The desktop modal says it is verifying age: its title is “Verify your age with BotShield”, the text reads “meridianairlines.com is asking BotShield to verify your age. BotShield never shares your identity — only an age result.”, and the QR code is labelled “Scan to verify your age with BotShield”.
The age signal is read on the phone, so an Age Gate never uses the desktop inline passkey. If the element has
betas="inline-passkey", the widget skips the passkey prompt by itself, shows the QR code, and dispatches botshield:inline-passkey with { status: "fallback", reason: "age_gate", request_id }.The flow from your side
What your server reads
On GET /verification/status
Age data belongs to Age Gates only. A Human Gate request never returns it: its token has no
age_over claim, and its age_threshold, age_verdict and age_source stay null, whatever the user’s phone can share.
create-verification-link also returns gate_type and age_threshold, so a server-to-server integration can label its own screen before the user confirms.
In the token
When a threshold is met, the attestation token carries one extra claim:
Compare with
>=, never with equality: allow when age_over >= your threshold.
POST /sdk/verify-token does not return age_over today. Its claims object lists only the standard claims. To read age_over, verify the JWT locally against the JWKS, or use age_verdict from GET /verification/status.A server check that fails closed
Both versions returntrue only when the age is positively established, and false for everything else: a bad token, a missing claim, an error, a timeout. Rely on the age fields only for requests where gate_type is age.
- From the token
- From the request status
Works whenever the success event carries a token, on desktop and on mobile. Verify within the token’s 120-second life.
request_id you accept and reject repeats.
Webhooks
An Age Gate sendsgate.human_verified when a human confirms, whatever the age result. The webhook payload does not carry age_verdict. When it arrives, call GET /verification/status with its request_id and read the age fields there.
Current limits
- Not in the SDK types.
botshield-sdk2.0.x does not typegate_type,age_threshold,age_verdictorage_source. CallGET /verification/statuswithfetchor curl, as above. - Not in the widget events.
botshield:successhas no age fields, and it fires when a human confirms even if the age isunavailable. - Not echoed by
verify-token. Decode the token locally to readage_over. - Needs the BotShield app on the phone. The age signal is read inside the BotShield app for iPhone and Android, which is in review. The web app (app.botshield.ai, public beta) completes the human check but has no platform age signal to read, so it returns
unavailablefor age. Older app builds do the same. - Needs a platform signal. The result is
unavailablewhen the platform has nothing to share: the operating system version does not provide the signal (on iPhone, Declared Age Range requires iOS 26 or later), the user declined to share it, the platform asks the user to confirm their age with it first, or the app was not installed from the platform’s store.
Design for Unavailable
Unavailable is a normal outcome for an Age Gate, because it depends on what the user’s phone platform can share. Plan for it:- Keep a fallback. Route Unavailable to the age check you use today. Treat the Age Gate as the fast path for users whose phone can answer.
- Never say “underage”. Unavailable means “not established”. Use neutral copy such as “We couldn’t confirm your age with BotShield.”
- Fail closed. For a restricted action, anything other than a positive result means no access on this proof.
- Let people retry. A user who declined to share their age range, or who needs to confirm their age with their platform first, can fix that and try again.
- Watch the rate. The gate’s Overview in the Console shows Over N Verified and Age Unavailable counts, and the Verification Logs label every row.
Next steps
Place a gate
Create the Age Gate in the Console.
Verify on your server
Token checks, status polling and the server-to-server flow.
Testing
See the age fields in the Console Sandbox.
Privacy boundary
What crosses to you, and what never does.
