Skip to main content

Age verification guide

Age verification with OpenAge is a privacy-preserving process that allows users to prove their age without revealing personal information. This approach uses a Waterfall flow model.

Waterfall flow

OpenAge acts as a single-point orchestrator for age checks, automatically cascading through a waterfall of verification providers to confirm a user's age. In practice, one API call to OpenAge presents the configured methods in sequence. For example, starting with email inference or a facial age estimation and then falling back to an ID document scan or other methods as needed, until the user's age is verified or all options are exhausted. This means developers integrate once with OpenAge's API, and the platform handles trying multiple verification techniques behind the scenes, combining methods to maximize the chances of a successful verification.

The verification flow is initiated with an API call that returns a URL to be hosted in an iframe or mobile web view, where users complete the verification process. The available verification methods are determined by your product configuration in the Compliance Studio, ensuring compliance with jurisdiction requirements.

OpenAge Explorer

The best way to get started with development is using the OpenAge Explorer, an open source tool for testing age verification flows. OpenAge Explorer lets you run through complete verification flows while monitoring all traffic, including DOM events, webhooks, and API calls in real time.

Initiating verification

OpenAge provides a set of APIs to verify the age of users for the following scenarios.

APIScenario
/age-verification/performTo verify the age of a user before getting access to a feature, mature content, or the product itself.
/age-verification/get-statusTo check the status of a verification request.

The Age Verification APIs are standardized in terms of the request & response format.

Request body

PropertyDescriptionRequired?
jurisdictionThe jurisdiction in which the age verification should happenYes
criteria.ageThe threshold age to pass the verification (integer)Yes*
criteria.ageCategoryThe age category. Must be either DIGITAL_YOUTH_OR_ADULT or ADULTYes*
subject.emailIf the user verified their age with OpenAge in any other context with an email address, then the original age is returned instead of asking the user to estimate or prove their age again.No
subject.idAn identifier used across multiple verification methods to report multiple failed attempts. This can be a temporary session ID, or hashed user ID.No
options.facialAgeEstimation.passIfOverThe estimated age threshold required to automatically pass facial age estimation. If the estimated age meets or exceeds this value, the verification passes.No
options.facialAgeEstimation.failIfUnderThe estimated age threshold below which the verification fails. If the estimated age is below this value, the verification fails. Defaults to the verification criteria age when omitted.No
options.redirectUrlThe URL to redirect to after verification completes. Supports HTTP/HTTPS URLs or mobile deeplinks with custom protocol schemes. The redirect only occurs when the verification URL is opened directly in a browser or web view (not embedded in an iframe). When a redirect occurs, the URL includes verificationId and result (PASS or FAIL) as query string parameters.No
note

*The criteria object must contain either age or ageCategory, but not both.

The passIfOver and failIfUnder parameters allow you to control the variance allowed in facial age estimation results. When a facial age estimation scan is performed:

  • If the estimated age meets or exceeds passIfOver, the verification passes and an age signal is determined.
  • If the estimated age is below failIfUnder, the verification fails and an age signal is determined.
  • If the estimated age is between failIfUnder and passIfOver, the result is considered inconclusive, and the user can retry facial age estimation.

This allows you to set a confidence range where results are clear enough to make a determination, while giving users the opportunity to retry when the estimate falls in an uncertain range. For example, if you need to verify users are 18+, you could set passIfOver to 25 and failIfUnder to 12. This means users estimated to be 25 or older pass immediately, users estimated to be under 12 fail immediately, and users estimated to be 12-24 can retry the scan or try other verification methods.

Sample:

{
"jurisdiction": "US-CA",
"subject": {
"email": "user@example.com",
"id": "3854909b-8888-4bed-9282-24b74c4a3c97"
},
"criteria": {
"ageCategory": "DIGITAL_YOUTH_OR_ADULT"
},
"options": {
"facialAgeEstimation": {
"passIfOver": 25,
"failIfUnder": 12
},
"redirectUrl": "https://example.com/verification-complete"
}
}

Redirect URL

The redirectUrl parameter allows you to specify where users should be redirected after completing verification. This is useful for:

  • Browser-based flows: Redirecting to another web page after verification completes
  • Mobile app integrations: Using deep links to return users to your mobile app after verification

The redirect only occurs when the verification URL is opened directly in a browser or web view (not embedded in an iframe). When embedded in an iframe, verification results are delivered via DOM events instead.

When a redirect occurs, the redirect URL includes the following query string parameters:

  • verificationId: The ID of the verification request
  • result: Either PASS or FAIL

Example redirect URL:

https://example.com/verification-complete?verificationId=7854909b-9124-4bed-9282-24b44c4a3c97&result=PASS

Response body

A successful request to the Age Verification API returns the following response.

PropertyDescription
idA unique verification identifier generated by the Age Verification Service
urlThe age verification URL that must be presented to the user, for them to verify themselves.

Sample:

{
"id": "7854909b-9124-4bed-9282-24b44c4a3c97",
"url": "https://ui.ageapi.org/verify?token=eyJhbGciOiJFUzM4NCIs..."
}

Verification flow

When a user needs to verify their age, the URL returned from the API call is presented to them in an iframe or mobile web view. The user is then presented with the verification methods that have been configured for your product in the Compliance Studio.

The available verification methods depend on your product configuration and the user's jurisdiction. For detailed information about each verification method, see the Verification Methods Guide.

Embedding the verification interface

Use the returned URL to create an iframe in your website or app. Users complete their verification through this interface, with available methods automatically adapting to jurisdictional requirements.

<div id="verification-container">
<iframe
id="verification-widget"
src="VERIFICATION_URL"
width="100%"
height="600"
frameborder="0"
allow="camera;payment;publickey-credentials-get;publickey-credentials-create">
</iframe>
</div>

The allow attribute is required to enable the following features:

  • camera: Required for facial age estimation
  • payment: Required for credit card verification
  • publickey-credentials-get and publickey-credentials-create: Required for AgeKey verification

Verification result

Once the user has successfully completed the age verification, or the user has retried the maximum number of times and hasn't succeeded, the Age Verification Result is delivered through both client-side and server-side channels. Implementations should use a combination of both: client-side events are best for controlling UI elements, while for data integrity, the actual results should come from either a webhook or a call to /age-verification/get-status.

Client-side (DOM events) - If the URL from the response body is included in an iframe, it's sent to the parent frame as a window message (MessageEvent) with a Verification.Result structure.

Server-side (webhooks) - An event is sent to the registered webhook in the form of a Verification.Result event.

Example of accessing the window message:

const handleMessage = (event: MessageEvent) => {
const message = event.data;
if (message.eventType === "Verification.Result") {
// Use DOM Events for immediate UI updates
updateUI(message);
}
};

window.addEventListener("message", handleMessage);
important

For data integrity, always verify results with events from webhooks or by calling /age-verification/get-status rather than relying solely on DOM Events. DOM Events are best suited for responsive UI updates.

The data element of the window event and the webhook event contains the following properties.

PropertyDescription
idThe verification identifier for which this is the result.
statusIndicates a PASS or FAIL status, based on whether the user met the age criteria or not.
ageCategoryIndicates the age category the user belongs to in the jurisdiction specified in the request. Supported values are adult, digital-youth or digital-minor
methodIndicates the method used for the verification. Supported values are id-document, age-estimation, age-attestation, credit-card, social-security-number
failureReasonThe reason the verification failed. Supported values are age-criteria-not-met, max-attempts-exceeded, or fraudulent-activity-detected. This is only set if status is FAIL
ageReturns the lower bound and higher bound of the estimated or verified age as low and high.

Sample:

{
"eventType": "Verification.Result",
"data": {
"id": "5a58e98a-e477-484b-b36a-3857ea9daaba",
"status": "PASS",
"ageCategory": "adult",
"method": "id-document",
"age": {
"low": 25,
"high": 25,
}
}
}

Handling a window event:

const handleMessage = (event: MessageEvent) => {
const message = event.data;
if (message.eventType === "Verification.Result") {
if (message.data.status === "PASS") {
window.location.href = `https://www.example.com/success?verificationId=${message.data.id}`
}
if (message.data.status === "FAIL") {
window.location.href = `https://www.example.com/fail?verificationId=${message.data.id}`
}
}
};

window.addEventListener("message", handleMessage);

Verification error

If an unexpected error has occurred, a JavaScript event fires so that your implementation can gracefully handle the error.

Sample Message:

{
"eventType": "Verification.Error",
"method": "credit-card",
"status": "ERROR"
}

Checking verification status

In addition to sending events both in JavaScript and through the OpenAge webhook, it's also possible to query for status for a verification. This is useful to be able to handle cases where the registered webhook was unreachable for a period of time, and the status event was never sent. To get status for a verification, use the /age-verification/get-status API. The data structure returned is identical to the data element sent to the webhook.

Limiting verification attempts

Each verification request allows users three attempts per available verification method. A verification fails if:

  • All available verification methods have been exhausted and an age can't be determined
  • An age is determined but falls below the required threshold for your criteria

When a verification fails, you can allow users to initiate a new verification attempt. However, to prevent misuse and abuse of the verification system, you should implement rate limiting on additional verification attempts. For example, you might limit users to three verification attempts within a 24-hour period.

Use the subject.id field in the verification request to track attempts across multiple verification requests. This field should contain a consistent identifier for the user (such as a temporary session ID or hashed user ID), allowing you to:

  • Track the number of verification attempts per user
  • Implement time-based rate limiting (for example, 3 attempts per 24 hours)
  • Prevent users from bypassing limits by creating new sessions
Best practice

Implement rate limiting on your server before initiating verification requests. This prevents unnecessary API calls and helps protect your system from abuse.

Verification methods

For detailed information about all available verification methods, see Verification methods.