Integration guide

AI Tools

After completing the Yoti onboarding, you will need to complete authentication for the API by using the Yoti SDK to simplify the process.

SDK Integration

Yoti backend 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>4.1.0</version> </dependency> // If you are using Gradle, add the following dependency: compile group: 'com.yoti', name: 'yoti-sdk-api', version: '4.1.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
// Simply add this as an import: import "github.com/getyoti/yoti-go-sdk/v3" // Or add the following line to your go.mod file require github.com/getyoti/yoti-go-sdk/v3 // Or you can run the following command in your terminal: go get github.com/getyoti/yoti-go-sdk/v3

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. Yoti strongly recommends you couple this with liveness.

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" // or laptop }, "secure": { "version": "<module version>", "token": "<session jwt>", "signature": "<payload>", "verification": "<verification_data>" } }; 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_LIVENESS) // optionally AGE or 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; $payload = [ "img" => "base64_image", "metadata" => ["device" => "mobile"], // or laptop "secure" => [ "version" => "<module version>", "token" => "<session jwt>", "signature" => "<payload>", "verification" => "<verification_data"> ], ]; $request = (new RequestBuilder()) ->withBaseUrl('https://api.yoti.com/ai/v1') ->withPemFilePath('<YOTI_KEY_FILE_PATH>') ->withEndpoint('/age-antispoofing') ->withPayload(Payload::fromJsonData($payload)) // For version < 3, use ->withPayload(new Payload($payload)) ->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" // # or laptop }, "secure": { "version": "<module version>", "token": "<session jwt>", "signature": "<payload>", "verification": "<verification_data>" } } 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); var metadata = new { device = "mobile", // or laptop }; var secure = new { version = "<module version>", token = "<session jwt>", signature = "<payload>", verification = "<verification_data>" }; var bodyObj = new { img = _base64Face, metadata = metadata, secure = secure }; string serializedRequest = Newtonsoft.Json.JsonConvert.SerializeObject(bodyObj); 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) .Build(); HttpResponseMessage response = ageScanRequest.Execute(httpClient).Result;
import ( "io/ioutil" "net/http" "github.com/getyoti/yoti-go-sdk/v3/requests" ) key, _ := ioutil.ReadFile("<YOTI_KEY_FILE_PATH>") imageBase64 := base64.StdEncoding.EncodeToString("<base64_image>") metadata := map[string]interface{}{ "device":"mobile" // or laptop } secure := map[string]interface{}{ "version":"<module_version>", "token":"<session jwt>", "signature":"<payload>", "verification":"<verification_data>" } bodyObj := map[string]interface{}{ "img": imageBase64, "metadata": metadata, "secure": secure, } payload, _ := json.Marshal(bodyObj) // Create session request,_ := requests.SignedRequest{ HTTPMethod: http.MethodPost, BaseURL: "https://api.yoti.com/ai/v1", Endpoint: "/antispoofing", Headers: map[string][]string{ "Content-Type": {"application/img"}, "Accept": {"application/img"}, "X-Yoti-Auth-Id":{"<YOTI_CLIENT_SDK_ID>"}, }, Body: payload, }.WithPemFile(key).Request() //get Yoti response response, _ := http.DefaultClient.Do(request)

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" // or laptop }, "secure": { "version": "<module_version>", "token": "<session_token>", "signature": "<result_signature>", "verification": "<verification_data>" } }

Parameter

Description

img

Base64 encoded image.

metadata

(optional) Device used to send the request (can be a string of unknown, laptop, mobile or any value).

secure

Secure information required by Secure Image Capture (SICAP) that certifies the image was not modified. The Yoti Face Capture Module provides this and shouldn't be edited.

Multiframe

If you are using Multiframe in the Face Capture Module (FCM), you must add the query parameter multiframe=true to your backend API request. Note that the payload size will be slightly larger when Multiframe is enabled.

If the Multiframe setting in the FCM does not match the multiframe query parameter in your 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" // or fake }, "age": { "st_dev": float, "age": float } }

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.

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.