Skip to main content
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.
An Age Gate is a gate that answers one more question than a Human Gate: is this human over the threshold? You choose the threshold, 13, 18 or 21, when you place the gate. The integration is the same as a Human Gate. The difference is one extra check on your server.
“Verified” does not mean the age was met. An Age Gate request finishes with status completed, a token with verified: true, a botshield:success event and a gate.human_verified webhook whenever a human confirms, even when the age result is unavailable. Those signals only tell you a real human was there. Your server must read the age result itself: age_verdict on GET /verification/status, or the age_over claim in the token. If it is missing or unavailable, the age was not established.

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.
The app reduces the platform’s answer to a yes for each threshold it clears and discards the rest on the device. Only “over 13”, “over 18” or “over 21” leaves the phone, and only when true. BotShield then combines that with the live proof that a real human is holding the phone. 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”.
Embed it like any gate:
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 return true 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.
Works whenever the success event carries a token, on desktop and on mobile. Verify within the token’s 120-second life.
Also apply the general checks from Verify on your server: record each request_id you accept and reject repeats.

Webhooks

An Age Gate sends gate.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-sdk 2.0.x does not type gate_type, age_threshold, age_verdict or age_source. Call GET /verification/status with fetch or curl, as above.
  • Not in the widget events. botshield:success has no age fields, and it fires when a human confirms even if the age is unavailable.
  • Not echoed by verify-token. Decode the token locally to read age_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 unavailable for age. Older app builds do the same.
  • Needs a platform signal. The result is unavailable when 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.