Sandbox

AI Tools

The Yoti In-Branch Verification (IBV) Sandbox is an isolated testing environment for validating your integration with mock data and simulated verification outcomes.

What you can do

  • Test end-to-end flows without real user data

  • Simulate different verification outcomes (approvals, rejections, extractions)

  • Use standard Yoti backed SDKs (no separate sandbox SDK)

  • Manually test using the user view, or automate testing (via built-in agent) by bypassing the in-branch experience.

Key differences

Sandbox uses the same SDKs as production but requires:

  • Sandbox URL: https://api.yoti.com/sandbox/idverify/v1

  • Sandbox keys from Yoti Hub

  • Predefined successful responses by default

  • Optional response configuration (see Configure response)


Before you start

Ensure you have a verified Yoti Hub account and have generated sandbox keys.

Sandbox keys


Install the SDK

Install the Yoti SDK using your language's package manager. The same SDK is used for both production and sandbox environments.

npm install -S -E yoti
// Get the Yoti PHP SDK library via a Composer package composer require yoti/yoti-php-sdk
pip install yoti
// If you are using Maven, add the following dependency: <dependency> <groupId>com.yoti</groupId> <artifactId>yoti-sdk-impl</artifactId> <version>3.10.0</version> </dependency> // If you are using Gradle, add the following dependency: compile group: 'com.yoti', name: 'yoti-sdk-impl', version: '3.10.0'
go get "github.com/getyoti/yoti-go-sdk/v2"

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 of how to construct the request.


Step 1: Create the session

Create an in-branch verification session exactly as you would in production. The session configuration determines what checks and tasks will be performed.

Endpoint:

https://api.yoti.com/sandbox/idverify/v1
const { RequestBuilder, Payload } = require("yoti"); const request = new RequestBuilder() .withBaseUrl("https://api.yoti.com/sandbox/idverify/v1") .withPemFilePath("<YOTI_KEY_FILE_PATH>") // file path to PEM file .withEndpoint("/sessions") .withPayload(new Payload(SESSION_OBJ)) .withMethod("POST") .withQueryParam("sdkId", "<YOTI_CLIENT_SDK_ID>") .build(); //get Yoti response const response = await request.execute();
<?php use Yoti\Http\RequestBuilder; use Yoti\Http\Payload; $request = (new RequestBuilder()) ->withBaseUrl('https://api.yoti.com/sandbox/idverify/v1') ->withPemFilePath('<YOTI_KEY_FILE_PATH>') ->withEndpoint('/sessions') ->withPayload(Payload::fromString(SESSION_JSON_STRING)) // * Version ^2: new Payload(SESSION_OBJ) ->withMethod('POST') ->withQueryParam('sdkId', '<YOTI_CLIENT_SDK_ID>') ->build() //get Yoti Response ->execute(); // * For JSON data in Version >3 please use Payload::fromJsonData(SESSION_OBJ)
from yoti_python_sdk.http import SignedRequest, RequestHandler import json import requests def execute(request): response = requests.request( url=request.url, data=request.data, headers=request.headers, method=request.method) return response.content def generate_session(): payload = {} # payload payload_string = json.dumps(payload).encode() signed_request = ( SignedRequest .builder() .with_pem_file("<YOTI_KEY_FILE_PATH>") .with_base_url("https://api.yoti.com/sandbox/idverify/v1") .with_endpoint("/sessions") .with_http_method("POST") .with_param("sdkId", "<YOTI_CLIENT_SDK_ID>") .with_payload(payload_string) .build() ) # get Yoti response response = signed_request.execute() response_payload = json.loads(response.text)
byte[] SESSION_OBJ = ... try { SignedRequest signedRequest = SignedRequestBuilder.newInstance() .withKeyPair(<YOTI_KEY_FILE_PATH>) .withBaseUrl("https://api.yoti.com/sandbox/idverify/v1") .withEndpoint("/sessions") .withPayload(SESSION_OBJ) .withHttpMethod("POST") .withQueryParameter("sdkId", "<YOTI_CLIENT_SDK_ID>") .build(); YourPojo yourPojo = signedRequest.execute(YourPojo.class); } catch (GeneralSecurityException | URISyntaxException | IOException | ResourceException ex) { ex.printStackTrace(); }
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/sandbox/idverify/v1", Endpoint: "/sessions", Params: map[string]string{ "sdkId": "<YOTI_CLIENT_SDK_ID>" }, Headers: map[string][]string{ "Content-Type": {"application/json"}, "Accept": {"application/json"} }, Body: func(data []byte, _ error) []byte { return data }(json.Marshal(jsonobj{ data }, })), }.WithPemFile(key).Request() //get Yoti response response, _ := http.DefaultClient.Do(request)

Example Session Creation

{ "session_deadline": "2023-10-15T22:00:00Z", "resources_ttl": "604800", "ibv_options": { "support": "MANDATORY" }, "user_tracking_id": "optional_string", "notifications": { "endpoint": "https://some-domain.example", "topics": [ "INSTRUCTIONS_EMAIL_REQUESTED", "THANK_YOU_EMAIL_REQUESTED", "SESSION_COMPLETION" ] }, "requested_checks": [ { "type": "IBV_VISUAL_REVIEW_CHECK", "config": { "manual_check": "IBV" } }, { "type": "PROFILE_DOCUMENT_MATCH", "config": { "manual_check": "IBV" } }, { "type": "DOCUMENT_SCHEME_VALIDITY_CHECK", "config": { "manual_check": "IBV", "scheme": "UK_DBS" } } ], "required_documents": [ { "type": "ID_DOCUMENT", "filter": { "type": "DOCUMENT_RESTRICTIONS", "inclusion": "WHITELIST", "documents": [ { "country_codes": ["GBR"], "document_types": ["PASSPORT"] } ] } }, { "type": "ID_DOCUMENT", "filter": { "type": "DOCUMENT_RESTRICTIONS", "inclusion": "WHITELIST", "documents": [ { "country_codes": ["GBR"], "document_types": ["DRIVING_LICENCE"] } ] } }, { "type": "SUPPLEMENTARY_DOCUMENT", "document_types": ["UTILITY_BILL"], "country_codes": ["GBR"], "objective": { "type": "UK_DBS" } } ], "resources": { "applicant_profile": { "full_name": "John James Doe", "date_of_birth": "1988-11-02", "structured_postal_address": { "address_format": 1, "building_number": "74", "address_line1": "74 First Address Line", "town_city": "London", "postal_code": "E14 3RN", "country_iso": "GBR", "country": "United Kingdom" } } } }
{ "session_deadline": "2023-10-15T22:00:00Z", "resources_ttl": "604800", "user_tracking_id": "some-tracking-id", "ibv_options": { "required": true, "support": "MANDATORY", "guidance_url": "https://yourDomain" }, "notifications": { "endpoint": "https://yourDomain", "topics": [ "RESOURCE_UPDATE", "TASK_COMPLETION", "CHECK_COMPLETION", "SESSION_COMPLETION", "NEW_PDF_SUPPLIED", "INSTRUCTIONS_EMAIL_REQUESTED" ], "auth_token": "xxx", "auth_type": "BASIC" }, "requested_checks": [ { "type": "ID_DOCUMENT_AUTHENTICITY", "config": { "manual_check": "ALWAYS" } }, { "type": "ID_DOCUMENT_FACE_MATCH", "config": { "manual_check": "FALLBACK" } } ], "requested_tasks": [ { "type": "ID_DOCUMENT_TEXT_DATA_EXTRACTION", "config": { "manual_check": "FALLBACK", "chip_data": "DESIRED" } } ], "required_documents": [ { "type": "ID_DOCUMENT", "filter": { "type": "ORTHOGONAL_RESTRICTIONS", "country_restriction": { "inclusion": "WHITELIST", "country_codes": ["GBR"] }, "type_restriction": { "inclusion": "WHITELIST", "document_types": ["PASSPORT"] } } } ] }
{ "session_deadline": "2023-10-15T22:00:00Z", "resources_ttl": "604800", "ibv_options": { "support": "MANDATORY" "user_price": { "amount": "0", "currency": "GBP" } }, "user_tracking_id": "some_id", "notifications": { "endpoint": "https://some-domain.example", "topics": [ "SESSION_COMPLETION" ] }, "requested_checks": [ { "type": "ID_DOCUMENT_AUTHENTICITY", "config": { "manual_check": "ALWAYS" } }, { "type": "ID_DOCUMENT_FACE_MATCH", "config": { "manual_check": "FALLBACK" } }, { "type": "IBV_VISUAL_REVIEW_CHECK", "config": { "manual_check": "IBV" } }, { "type": "PROFILE_DOCUMENT_MATCH", "config": { "manual_check": "IBV" } }, { "type": "DOCUMENT_SCHEME_VALIDITY_CHECK", "config": { "manual_check": "IBV", "scheme": "UK_DBS" } } ], "requested_tasks": [ { "type": "ID_DOCUMENT_TEXT_DATA_EXTRACTION", "config": { "manual_check": "FALLBACK" } } ], "required_documents": [ { "type": "ID_DOCUMENT", "filter": { "type": "DOCUMENT_RESTRICTIONS", "inclusion": "WHITELIST", "documents": [ { "country_codes": [ "GBR" ], "document_types": [ "PASSPORT" ] } ] } }, { "type": "ID_DOCUMENT", "filter": { "type": "DOCUMENT_RESTRICTIONS", "inclusion": "WHITELIST", "documents": [ { "country_codes": [ "GBR" ], "document_types": [ "DRIVING_LICENCE" ] } ] } }, { "type": "SUPPLEMENTARY_DOCUMENT", "document_types": [ "UTILITY_BILL" ], "country_codes": [ "GBR" ], "objective": { "type": "UK_DBS" } } ], "resources": { "applicant_profile": { "full_name": "John James Doe", "date_of_birth": "1988-11-02", "structured_postal_address": { "address_format": 1, "building_number": "74", "address_line1": "74 First Address Line", "town_city": "London", "postal_code": "E14 3RN", "country_iso": "GBR", "country": "United Kingdom" } } } }

Example Response

If the request is successful and a session is generated the API will send a response in the form:

{ "client_session_token_ttl": 599, "client_session_token": "<uuid>", "session_id": "<uuid>" }

Response

Description

client_session_token_ttl

Time in seconds until the client session expires

client_session_token

Used to authenticate the session

session_id

ID of the session

Step 2: Configure sandbox response

Now that you have successfully created a session, the next step is to set the response config. This is an optional step. If no response is configured, Yoti will use a default successful response. But you do have the option to set an expected outcome for your Yoti tasks and checks. This will be an object with defined task_results and check_reports.

Endpoint:

https://api.yoti.com/sandbox/idverify/v1/sessions/<SESSION_ID>/response-config
const { RequestBuilder, Payload } = require("yoti"); const request = new RequestBuilder() .withBaseUrl("https://api.yoti.com/sandbox/idverify/v1") .withPemFilePath("<YOTI_KEY_FILE_PATH>") .withEndpoint("/sessions/<SESSION_ID>/response-config") .withPayload(new Payload(RESPONSE_CONFIG)) .withMethod("PUT") .withQueryParam("sdkId", "<YOTI_CLIENT_SDK_ID>") .build(); //get Yoti response const response = request.execute();
<?php use Yoti\Http\RequestBuilder; use Yoti\Http\Payload; $request = (new RequestBuilder()) ->withBaseUrl('https://api.yoti.com/sandbox/idverify/v1') ->withPemFilePath('<YOTI_KEY_FILE_PATH>') ->withEndpoint('/sessions/<SESSION_ID>/response-config') ->withPayload(Payload::fromString(RESPONSE_CONFIG_STRING)) // * Version ^2: new Payload(SESSION_OBJ) ->withMethod('PUT') ->withQueryParam('sdkId', '<YOTI_CLIENT_SDK_ID>') ->build() //get Yoti Response ->execute();
from yoti_python_sdk.http import SignedRequest, RequestHandler import json import requests def execute(request): response = requests.request( url=request.url, data=request.data, headers=request.headers, method=request.method) return response.content def generate_session(): payload_string = json.dumps(data).encode() signed_request = ( SignedRequest .builder() .with_pem_file("https://api.yoti.com/sandbox/idverify/v1") .with_base_url("<BASE_URL>") .with_endpoint("/sessions/<SESSION_ID>/response-config") .with_http_method("PUT") .with_query_param("sdkId", "<YOTI_CLIENT_SDK_ID>") .with_payload(payload_string) .build() ) # get Yoti response response = signed_request.execute() response_payload = json.loads(response.text)
byte[] RESPONSE_CONFIG = ... try { SignedRequest signedRequest = SignedRequestBuilder.newInstance() .withKeyPair(<YOTI_KEY_FILE_PATH>) .withBaseUrl("https://api.yoti.com/sandbox/idverify/v1") .withEndpoint("/sessions/<SESSION_ID>/response-config") .withPayload(RESPONSE_CONFIG) .withHttpMethod("PUT") .withQueryParameter("sdkId", "<YOTI_CLIENT_SDK_ID>") .build(); YourPojo yourPojo = signedRequest.execute(YourPojo.class); } catch (GeneralSecurityException | URISyntaxException | IOException | ResourceException ex) { ex.printStackTrace(); }// Click to edit code
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.MethodPut, BaseURL: "<BASE_URL>", Endpoint: "/sessions/<SESSION_ID>/response-config", Params: map[string]string{ "sdkId": "<YOTI_CLIENT_SDK_ID>" }, Headers: map[string][]string{ "Content-Type": {"application/json"}, "Accept": {"application/json"} }, Body: func(data []byte, _ error) []byte { return data }(json.Marshal(jsonobj{ data }, })), }.WithPemFile(key).Request() //get Yoti response response, _ := http.DefaultClient.Do(request)

Example Response Configuration

{ "check_reports": { "IBV_VISUAL_REVIEW_CHECK": [ { "result": { "report_template": "SUCCESS" } } ], "DOCUMENT_SCHEME_VALIDITY_CHECK": [ { "result": { "report_template": "SUCCESS" } } ], "PROFILE_DOCUMENT_MATCH": [ { "result_template": "SUCCESS" } ] } }
{ "task_results": { "ID_DOCUMENT_TEXT_DATA_EXTRACTION": [ { "result_template": "SUCCESS" } ] }, "check_reports": { "ID_DOCUMENT_AUTHENTICITY": [ { "result": { "report_template": "SUCCESS" } } ], "ID_DOCUMENT_FACE_MATCH": [ { "result": { "report_template": "SUCCESS" } } ] } }
{ "task_results": { "ID_DOCUMENT_TEXT_DATA_EXTRACTION": [ { "result_template": "SUCCESS" } ] }, "check_reports": { "ID_DOCUMENT_AUTHENTICITY": [ { "result": { "report_template": "SUCCESS" } } ], "ID_DOCUMENT_FACE_MATCH": [ { "result": { "report_template": "SUCCESS" } } ], "IBV_VISUAL_REVIEW_CHECK": [ { "result": { "report_template": "SUCCESS" } } ], "DOCUMENT_SCHEME_VALIDITY_CHECK": [ { "result": { "report_template": "SUCCESS" } } ], "PROFILE_DOCUMENT_MATCH": [ { "result_template": "SUCCESS" } ] } }

On completion of this API request, you will simply receive a 200 response.

In the sandbox you can configure both successful outcomes of checks and unsuccessful outcomes of checks to mock multiple different scenarios. The below table outlines the different scenarios that can be mocked.

Checks

Check

Report template

Description

ID_DOCUMENT_AUTHENTICITY

SUCCESS

This simulates a "APPROVE" outcome for the document authenticity check

ID_DOCUMENT_AUTHENTICITY

TAMPERED

This simulates a "REJECT" outcome for the document authenticity check, due to a fraudulent document.

ID_DOCUMENT___AUTHENTICITY

DOCUMENT_COPY

This simulates a "REJECT" outcome for the document authenticity check, due to an image of a document being used instead of a live capture.

ID_DOCUMENT_AUTHENTICITY

PHOTO_TOO_BLURRY

This simulates a "NOT_AVAILABLE" outcome for the document authenticity check, due a blurry image.

ID_DOCUMENT_AUTHENTICITY

FRAUD_LIST_MATCH

This simulates a "REJECT" outcome for the document authenticity check, due to a fraudulent document.

ID_DOCUMENT_AUTHENTICITY

GLARE_OBSTRUCTION

This simulates a "NOT_AVAILABLE" outcome for the document authenticity check, due to glare obstruction.

ID_DOCUMENT___FACE_MATCH

SUCCESS

This simulates a "APPROVE" outcome for the face match check.

ID_DOCUMENT___FACE_MATCH

AUTO_FAIL

This simulates a "REJECT" outcome for the face match check. With the automated sub check failing.

ID_DOCUMENT_FACE_MATCH

MANUAL_FAIL

This simulates a "REJECT" outcome for the face match check. With the manual sub check failing.

ID_DOCUMENT_FACE_MATCH

FAIL

This simulates a "REJECT" outcome for the face match check. With both the automated and the manual sub check failing.

ID_DOCUMENT_TEXT_DATA_EXTRACTION

SUCCESS

This simulates a "APPROVE" outcome for the manual data extraction.

ID_DOCUMENT_TEXT_DATA_EXTRACTION

EXTRACTION_FAILED

This simulates a "REJECT" outcome for the manual data extraction.

Tasks

Task

Report template

Description

ID_DOCUMENT_TEXT_DATA_EXTRACTION

SUCCESS

This simulates a successful automated data extraction task.

ID_DOCUMENT_TEXT_DATA_EXTRACTION

UNSUPPORTED_DOCUMENT

This simulates an unsuccessful automated data extraction task.

ID_DOCUMENT_TEXT_DATA_EXTRACTION

DOCUMENT_IS_EXPIRED

This simulates a unsuccessful automated data extraction task.

ID_DOCUMENT_TEXT_DATA_EXTRACTION

UNABLE_TO_OCR

This simulates a unsuccessful automated data extraction task.

Step 3: Bypass In-Branch experience

Once you have set your sandbox response config, bypass the user view by making an API call to mock the user flow. The session Id and session token created in the earlier create session step must be used in the payload for this API request to work.

Endpoint

POST https://api.yoti.com/sandbox/idverify/v1/agent

Example Request

const { RequestBuilder, Payload } = require("yoti"); const request = new RequestBuilder() .withBaseUrl("https://api.yoti.com/sandbox/idverify/v1") .withPemFilePath("<YOTI_KEY_FILE_PATH>") // file path to PEM file .withEndpoint("/agent") .withPayload(new Payload(SKIP_UI)) .withMethod("POST") .withQueryParam("sdkId", "<YOTI_CLIENT_SDK_ID>") .build(); //get Yoti response const response = await request.execute();
<?php use Yoti\Http\RequestBuilder; use Yoti\Http\Payload; $request = (new RequestBuilder()) ->withBaseUrl('https://api.yoti.com/sandbox/idverify/v1') ->withPemFilePath('<YOTI_KEY_FILE_PATH>') ->withEndpoint('/agent') ->withPayload(Payload::fromString(SKIP_UI)) // * Version ^2: new Payload(SKIP_UI) ->withMethod('POST') ->withQueryParam('sdkId', '<YOTI_CLIENT_SDK_ID>') ->build() //get Yoti Response ->execute(); // * For JSON data in Version >3 please use Payload::fromJsonData(SKIP_UI)
from yoti_python_sdk.http import SignedRequest, RequestHandler import json import requests def execute(request): response = requests.request( url=request.url, data=request.data, headers=request.headers, method=request.method) return response.content def generate_session(): payload = {} # payload payload_string = json.dumps(payload).encode() signed_request = ( SignedRequest .builder() .with_pem_file("<YOTI_KEY_FILE_PATH>") .with_base_url("https://api.yoti.com/sandbox/idverify/v1") .with_endpoint("/agent") .with_http_method("POST") .with_param("sdkId", "<YOTI_CLIENT_SDK_ID>") .with_payload(payload_string) .build() ) # get Yoti response response = signed_request.execute() response_payload = json.loads(response.text)
byte[] SKIP_UI = ... try { SignedRequest signedRequest = SignedRequestBuilder.newInstance() .withKeyPair(<YOTI_KEY_FILE_PATH>) .withBaseUrl("https://api.yoti.com/sandbox/idverify/v1") .withEndpoint("/agent") .withPayload(SKIP_UI) .withHttpMethod("POST") .withQueryParameter("sdkId", "<YOTI_CLIENT_SDK_ID>") .build(); YourPojo yourPojo = signedRequest.execute(YourPojo.class); } catch (GeneralSecurityException | URISyntaxException | IOException | ResourceException ex) { ex.printStackTrace(); }
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/sandbox/idverify/v1", Endpoint: "/agent", Params: map[string]string{ "sdkId": "<YOTI_CLIENT_SDK_ID>" }, Headers: map[string][]string{ "Content-Type": {"application/json"}, "Accept": {"application/json"} }, Body: func(data []byte, _ error) []byte { return data }(json.Marshal(jsonobj{ data }, })), }.WithPemFile(key).Request() //get Yoti response response, _ := http.DefaultClient.Do(request)

Payload Example

{ "session_id": "YOUR_SESSION_ID", "client_session_token": "YOUR_SESSION_TOKEN", }


  Last updated by Josh Steadman