Age Estimation v2

AI Tools

Once you've set up your organisation account on the Yoti Hub, you’re ready to start integrating with the Age estimation and anti-spoofing (liveness) API.

Before you start

Please have a read of our image requirements.

Image requirements


Face Capture

For optimal results, Yoti provides the Yoti Face Capture modules, which are designed to capture optimal images meeting the requirements of the API. This is available as a Native package, and Web implementation.

Strongly recommend
Please contact Yoti if you opt to not use the Face capture module.

Integration

Install the SDK

After completing the Yoti onboarding, you will need to complete authentication for the API by using the Yoti SDK to simplify the process. The Yoti SDKs are available via popular dependency management systems.

npm install -S -E yoti
// If you are using Maven, add the following dependency: <dependency> <groupId>com.yoti</groupId> <artifactId>yoti-sdk-api</artifactId> <version>3.11.0</version> </dependency> // If you are using Gradle, add the following dependency: compile group: 'com.yoti', name: 'yoti-sdk-api', version: '3.11.0'
// Get the Yoti PHP SDK library via a Composer package composer require yoti/yoti-php-sdk
pip install yoti
// To install the Yoti NuGet package you will need to install NuGet. // To import the latest Yoti SDK into your project, enter the following // command from NuGet Package Manager Console in Visual Studio: Install-Package Yoti // For other installation methods, see https://www.nuget.org/packages/Yoti
// As of version 2.4.0, modules are used. This means it's not necessary to get a copy or fetch all dependencies // as instructed below, as the Go toolchain will fetch them as necessary. // You can simply add a `require github.com/getyoti/yoti-go-sdk/v2` to go.mod. // To download and install the Yoti SDK and its dependencies, run the following command from your terminal: go get "github.com/getyoti/yoti-go-sdk/v3"
gem install yoti # The gem provides a configuration generator for Ruby on Rails: rails generate yoti:install

Create a request

You can create three types of requests using the API:

Endpoint

Description

https://api.yoti.com/ai/v1/age?secure=true

Use Yoti's Age estimation service.

https://api.yoti.com/ai/v1/antispoofing?secure=true

Use Yoti's Anti-spoofing (liveness) check.

https://api.yoti.com/ai/v1/age-antispoofing?secure=true

Use Yoti's Age estimation service and the Anti-spoofing (liveness) check.

Once you have added the Yoti SDK dependency to your project, you can use it to build and send your request. See the code snippets below for examples.

const PATHS = { AGE: '/age', LIVENESS: '/antispoofing', AGE_LIVENESS: '/age-antispoofing', }; const data = { "img": "base64_image", "metadata": { "device": "mobile | laptop" }, "secure": { "version": "<module version>", "token": "<session jwt>", "signature": "<payload>" } }; const request = new RequestBuilder() .withBaseUrl('https://api.yoti.com/ai/v1') .withPemFilePath('<YOTI_KEY_FILE_PATH>') .withEndpoint(PATHS.AGE_LIVENESS) // optionally PATHS.AGE or PATHS.LIVENESS .withPayload(new Payload(data)) .withMethod('POST') .withHeader('X-Yoti-Auth-Id', '<YOTI_CLIENT_SDK_ID>') .withQueryParam('secure', true) .build(); const response = request.execute();
import java.io.File; import org.json.JSONObject; import com.yoti.api.client.spi.remote.call.SignedRequest; import com.yoti.api.client.spi.remote.call.SignedRequestBuilderFactory; import com.yoti.api.client.spi.remote.call.SignedRequestResponse; import com.yoti.api.client.KeyPairSource; import com.yoti.api.client.spi.remote.KeyStreamVisitor; import static com.yoti.api.client.FileKeyPairSource.fromFile; private final KeyPairSource fileKeyPairSource; private final java.security.KeyPair keyPair; String AGE = "/age"; String LIVENESS = "/antispoofing"; String AGE_LIVENESS = "/age-antispoofing"; this.fileKeyPairSource = fromFile(new File("<PEM_FILE_PATH>")); this.keyPair = fileKeyPairSource.getFromStream(new KeyStreamVisitor()); JSONObject body = new JSONObject(); body.put("img", requestPayload.get("img")); body.put("metadata", requestPayload.get("metadata")); // Optional, but recommended body.put("secure", requestPayload.get("secure")); byte[] payload = body.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8); SignedRequestBuilderFactory signedRequestBuilderFactory = new SignedRequestBuilderFactory(); SignedRequest signedRequest = signedRequestBuilderFactory.create() .withKeyPair(keyPair) .withBaseUrl("https://api.yoti.com/ai/v1/") .withEndpoint(AGE) // AGE, LIVENESS or AGE_LIVENESS .withHttpMethod("POST") .withHeader("X-Yoti-Auth-Id", "<YOTI_CLIENT_SDK_ID>") .withQueryParameter("secure", true) .withPayload(payload) .build(); SignedRequestResponse response = signedRequest.execute(); int statusCode = response.getResponseCode(); byte[] responseBody = response.getResponseBody(); Map<String, List<String>> headers = response.getResponseHeaders();
<?php use Yoti\Http\RequestBuilder; use Yoti\Http\Payload; $img = [ "img" => "base64_image", "metadata" => ["device" => "mobile | laptop"], "secure" => [ "version" => "<module version>", "token" => "<session jwt>", "signature" => "<payload>", ], ]; $request = (new RequestBuilder()) ->withBaseUrl('https://api.yoti.com/ai/v1') ->withPemFilePath('<YOTI_KEY_FILE_PATH>') ->withEndpoint('/age-antispoofing') ->withPayload(Payload::fromJsonData($img)) // For version < 3, use ->withPayload(new Payload($img)) ->withMethod('POST') ->withHeader('X-Yoti-Auth-Id', '<YOTI_CLIENT_SDK_ID>') ->withQueryParam('secure', true) ->build(); // Execute request $response = $request->execute(); // Get response body $body = $response->getBody();
from yoti_python_sdk.http import SignedRequest, RequestHandler import json import requests def yoti_age_estimation(): data = { "img": "base64_image", "metadata": { "device": "mobile | laptop" }, "secure": { "version": "<module version>", "token": "<session jwt>", "signature": "<payload>" } } payload_string = json.dumps(data).encode() signed_request = ( SignedRequest .builder() .with_pem_file("<YOTI_KEY_FILE_PATH>") .with_base_url("https://api.yoti.com/ai/v1") .with_endpoint("/age-antispoofing") .with_http_method("POST") .with_header("X-Yoti-Auth-Id", "<YOTI_CLIENT_SDK_ID>") .with_param("secure", True) .with_payload(payload_string) .build() ) # get Yoti response response = signed_request.execute() response_payload = json.loads(response.text)
using System; using System.IO; using System.Net.Http; using System.Text; using Microsoft.AspNetCore.Mvc; using Yoti.Auth.Web; HttpClient httpClient = new HttpClient(); StreamReader privateKeyStream = System.IO.File.OpenText(_pemFilePath); string serializedRequest = Newtonsoft.Json.JsonConvert.SerializeObject(new { img = _base64Face }); byte[] byteContent = Encoding.UTF8.GetBytes(serializedRequest); Uri _baseUrl = new UriBuilder("https", "api.yoti.com", 443, "ai/v1").Uri; Yoti.Auth.Web.Request ageScanRequest = new RequestBuilder() .WithStreamReader(privateKeyStream) .WithBaseUri(_baseUrl) .WithEndpoint("/age-antispoofing") .WithHttpMethod(HttpMethod.Post) .WithContent(byteContent) .WithHeader("X-Yoti-Auth-Id", _clientSdkId) .WithQueryParam("secure", true") .Build(); HttpResponseMessage response = ageScanRequest.Execute(httpClient).Result;
import ( "io/ioutil" "net/http" "github.com/getyoti/yoti-go-sdk/v2/requests" ) key, _ := ioutil.ReadFile("<YOTI_KEY_FILE_PATH>") // Create session request,_ := requests.SignedRequest{ HTTPMethod: http.MethodPost, BaseURL: "https://api.yoti.com/ai/v1", Endpoint: "/age-antispoofing", Headers: map[string][]string{ "Content-Type": {"application/json"}, "Accept": {"application/json"}, "X-Yoti-Auth-Id":{"<YOTI_CLIENT_SDK_ID>"}, }, Params: map[string]string{ "secure": "true", }, Body: func(img []byte, _ error) []byte { return img }(json.Marshal(jsonobj{ data }, })), }.WithPemFile(key).Request() //get Yoti response response, _ := http.DefaultClient.Do(request)
require 'yoti' Yoti.configure do |config| config.client_sdk_id = '<YOTI_CLIENT_SDK_ID>' config.key_file_path = '<YOTI_KEY_FILE_PATH>' end request = Yoti::Request .builder .with_base_url('https://api.yoti.com/ai/v1') .with_http_method('POST') .with_endpoint('/age-antispoofing') .with_payload(img: 'base64Image') .with_header('X-Yoti-Auth-Id', Yoti.configuration.client_sdk_id) .with_query_param('secure', true) .build response = request.execute body = response.body puts body

The JSON string for the payload must be in the following format. This is sent with the withPayload method provided in the SDK. The face capture module will automatically return “img” and “secure” on success. Do not modify these fields manually.

{ "img": "base64_image", "metadata": { "device": "mobile | laptop" }, "secure": { "version": "<module version>", "token": "<session jwt>", "signature": "<payload>" } }

img is the mandatory parameter that contains the captured image in Base64 Encoded format.

metadata is an optional parameter but is strongly recommended for better results. It is used to specify what type of device is being used. You can choose between mobile or laptop.

secure is a mandatory parameter that is returned by the face capture module.

Multiframe

If you're using Multiframe within the FCM - you must include the query parameter multiframe=true

The payload size will increase when using Multiframe

If there is a mismatch between the Face Capture's multiframe setting and the multiframe query parameter in the request, the response will always be UNTRUSTED_SECURE_SESSION

Retrieve the results

The endpoint will return the Anti-spoofing result alongside Age estimation.

{ "antispoofing": { "prediction": "real | fake" }, "age": { "st_dev": float, "age": float } }

If requesting age only, the response will look as follows:

{ "age": float, "st_dev": float }

If integrating into a non-browser client, we recommend contacting us here for additional support.

Relaxed image requirements

When using the Age only endpoint, it is possible to lower the threshold for face detection and image validation on images. A reason for this might be that images are not being captured live through Yoti's Face Capture module, or you may want to age estimate images where there is no case to perform a liveness check.

This feature is utilised by setting the image validation level to low, as shown in the example payload.

Note that this may only be used with the age endpoint.

See this page for more details on the image requirements.

{ "img": "/9j/4AAQSkZJRgABAgAAAQABAAD/2wAcHNXCKigjEaYH51Me1Zy1Za0P/Z....", "img_validation_level": "low" }

Terminal integrations

Optionally, additional headers may be provided to the request to allow Yoti to track error responses coming from a particular machine. This would typically be used in a non-browser client scenario, such as an ePOS terminal where each individual machine must be registered by the business.

In order to do this, the following headers should be applied:

Header

Description

Terminal-Id

Unique ID per machine. Mandatory for non-browser integrations.

Session-Id

May be provided to demonstrate multiple attempts are from a single user transaction.

Example responses

The response received from the API will be below, depending on the endpoint used.

{ "antispoofing": { "prediction": "real" }, "age": { "age": 25.1, "st_dev": 3.5 } }
{ "age": 43.2, "st_dev": 4.4 }
{ "prediction": "real" }

Response

Explained

Prediction - real

Yoti has detected a real user.

Prediction - fake

Yoti has detected a spoof attempt.

Age - age

The age estimation of the user.

Age - st_dev

The st_dev value is a quality score. Yoti advises rejecting any response with a value higher than 6.0, as this usually suggests an issue with image capture.

Error codes

In case of any failure, you will receive the following error responses:

Error code

Error

Description

404

APP_NOT_FOUND

Application app_id not found.

401

INVALID_X_YOTI_AUTH_ID

  • X-Yoti-Auth-Id header not provided.

  • auth id is not a valid uuid.

400

INVALID_APP_ID

Application id cannot be empty.

400

INVALID_PUBLIC_KEY

Application public key cannot be empty.

403

DISABLED_APP_STATE

Application must be enabled.

400

INVALID_ORG_ID

Organisation id cannot be empty.

400

INVALID_BILLING_SOURCE_ID

Application billing source id cannot be empty.

404

ORG_NOT_FOUND

Organisation org_id not found.

401

INVALID_YOTI_AUTH_DIGEST

  • X-Yoti-Auth-Digest header is missing.

  • X-Yoti-Auth-Digest is not base64 encoded.

401

INVALID_NONCE

  • Nonce is missing. nonce parameter is mandatory

  • Provided nonce is not a valid uuid.

401

INVALID_TIMESTAMP

  • Timestamp is missing. timestamp parameter is mandatory.

  • Provided timestamp is not a valid unix timestamp.

401

INVALID_PUBLIC_KEY_ENCODING

Failed to load public key. key is not der encoded.

401

UNSUPPORTED_ALGORITHM

Serialised key is of a type that is not supported by the backend.

401

INVALID_SIGNATURE

Failed to verify signature.

403

INVALID_ORG_STATUS

Organisation has an invalid status.

404

INVALID_METADATA_DEVICE

Invalid device metadata provided.

400

INVALID_BODY_ENCODING

Request body should be a valid JSON.

404

INVALID_ENDPOINT

The endpoint request is invalid.

413

PAYLOAD_TOO_LARGE

Payload too large.

400

IMAGE_NOT_PROVIDED

Image has not been provided.

400

INVALID_B64_IMAGE

  • Base64 image is incorrectly padded.

  • Cannot create image from base64 decoded bytes

400

UNSUPPORTED_IMAGE_FORMAT

Image format not supported. Please use JPEGs (95 to 100 quality) and PNGs.

400

IMAGE_SIZE_TOO_BIG

Image size too big, the maximum size is 1.5MB.

400

IMAGE_SIZE_TOO_SMALL

Image size too small, the minimum size is 50KB.

400

MIN_HEIGHT

The image height is incorrect. Image minimum height required is 300 pixels.

400

MAX_HEIGHT

The image height is incorrect. Image maximum height required is 2000 pixels.

400

MIN_WIDTH

The image width is incorrect. Image minimum width required is 300 pixels.

400

MAX_WIDTH

The image width is incorrect. Image maximum width required is 2000 pixels.

400

MIN_PIXELS

To process the image the minimum number of pixels required is 90,000 pixels.

400

MAX_PIXELS

To process the image the maximum number of pixels required is 2,100,000 pixels.

400

IMAGE_WRONG_CHANNELS

Missing colour channel, the input image must be RGB or RGBA.

400

IMAGE_GRAYSCALE_NOT_SUPPORTED

Grayscale images not supported.

503

SERVICE_UNAVAILABLE

The service is temporarily unavailable.

400

FACE_NOT_FOUND

Face not found.

400

MULTIPLE_FACES

Multiple faces in the image provided.

400

FACE_BOX_TOO_SMALL

The face in the image provided is too small.

400

FACE_TO_IMAGE_RATIO_TOO_LOW

Face ratio is lower than the minimum ratio.

400

FACE_TO_IMAGE_RATIO_TOO_HIGH

Face ratio is bigger than the maximum ratio.

400

INSUFFICIENT_AREA_AROUND_THE_FACE

Insufficient area around the face in the image provided.

400

IMAGE_TOO_BRIGHT

Image too bright..

400

IMAGE_TOO_DARK

Image too dark.

400

INVALID_LEVEL_OF_ASSURANCE

Invalid antispoofing level of assurance provided.

400

INVALID_REQUEST_BODY

Request body is invalid, '-' field is invalid.

400

INVALID_IMG_VALIDATION_LEVEL

The image validation level is invalid.

401

UNAUTHORIZED

The X-Yoti-Auth-Id provided isn't authorized to access this resource.

500

FAIL_PREDICTION

  • Age distribution is empty.

  • Standard deviation is empty.

  • Cannot build model result.

  • Antispoofing prediction failed.

500

UNSPECIFIED_ERROR

An internal server error occurred.

400

SECURE_REQUEST_IS_EMPTY

Secure request field is empty.

400

SECURE_SESSION_NOT_FOUND

Secure session not found.

400

SECURE_SIGNATURE_NOT_FOUND

Secure signature not found.

400

SECURE_VERSION_NOT_FOUND

Secure version not found.

400

INVALID_SECURE_SIGNATURE

Failed to verify secure session signature.

400

SECURE_VERIFICATION_NOT_FOUND

Secure verification not found.

400

UNTRUSTED_SECURE_SESSION

Untrusted secure session

401

INVALID_SECURE_SESSION

Invalid secure session token.