Create button
Yoti provides a button generator for you to include in your HTML file. In the example below, the button generator script has been added to the head of the HTML document.
To generate the Yoti button please take a look at our range of Yoti buttons and pick a configuration.
This JavaScript library currently needs to be invoked – do this by calling Yoti.Share.init() in the body of your HTML document. For the config, you will need to specify a ‘domId’ so we know where we need to add the Yoti button on your page and the ‘scenarioId’ that is being provided by Yoti Hub after creating an application.
The Yoti button requires the hosting page to be accessed via HTTPS, so please make sure that your web application has HTTPS enabled.
Name | Purpose | Required |
---|---|---|
domID | Specifies the ID of the DOM node where Yoti webshare wants to be rendered. | ✅ |
clientSdkId | Identifies your Yoti Hub application. This value can be found in the Hub, within your application section, in the keys tab. | ✅ |
scenarioId | Identifies the attributes associated with your Yoti application. This value can be found on your application page in Yoti Hub (navigate to Scenarios tab) | ✅ |
Button | Config for the button styling. | ❌ |
Button - Label | Text on the button. Should be relevant for your use case. | ❌ |
Button - align | Alignment of the button in the parent div - "center", "left", "right" | ❌ |
Button - width | Width of the button - "auto" will set to fit the label. "full" - will fill the width of the parent div. | ❌ |
Modal - zIndex | Sets the z-index of the modal. This is defaulted to 9999. | ❌ |
shareComplete | Allows token handling to be performed without redirecting the webpage. | ❌ |
shareComplete - closeDelay | How long the modal will stay open before handling the token or redirecting. Default to 4000ms with a min of 500ms and a max of 1000ms | ❌ |
shareComplete - tokenHandler | Allows you to run your own function to handle the token without a redirect. Calling done() will close the modal; overwriting any closeDelay you have. | ❌ |
label | This is the text on the button and is customisable | ✅ |
title | This is the text around the QR code and is customisable | ❌ |
shareUrl | This is used with programmatic QR code creation. It allows you to render a Yoti QR created via the Yoti SDK. If using a shareUrl then you should not specify a scenarioId. | ❌ |
shareUrlProvider | shareUrlProvider is a function which resolves to a share url generated from using programmatic QR code creation. If using this method, a scenarioId should not be specified. Using this function allows us to retrieve a new programmatic QR from your server in the original QR times out. | ❌ |
displayLearnMoreLink | Creates a dynamic page which informs and gives context to your users of steps they need to complete to download Yoti and scan the QR code. Provided as a link below the button. This can be turned off by changing to false. | ❌ |
Finally, the domain port pair where the button is deployed (i.e. https://localhost:8000) must match the one that you have configured in Yoti Hub. This prevents other web sites from embedding your Yoti button.
Supported Translations (Locale)
Yoti offers the ability to force a language locale for the front end client - see list here:
Language | Parameter |
---|---|
English | en-GB (default) |
French (Français) | fr |
Spanish (Español) | es |
German (Deutsch) | de |
Czech (čeština) | cz |
Polish (polski) | pl |
Modal QR
The modal QR code option has a button which when clicked opens a modal pop out window with the QR code present. There are three tabs, describing how to scan the QR code, The QR code and attributes to be shared and about Yoti - Please give it a click to see it working:
<!-- Simple Button Generation -->
<head>
<script src="https://www.yoti.com/share/client/"></script>
</head>
<body>
<!-- Yoti element will be rendered inside this DOM node -->
<div id="xxx"></div>
<!-- This script snippet will also be required in your HTML body -->
<script>
window.Yoti.Share.init({
elements: [
{
domId: "xxx",
scenarioId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
clientSdkId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
displayLearnMoreLink: true,
}
]
});
</script>
</body>
Inline QR
The inline QR code option has a button which when clicked opens just the QR code. You will need to provide more context as to what Yoti is for this. See Scenario examples for more detail.

Inline qr code
<!-- Simple Button Generation -->
<head>
<script src="https://www.yoti.com/share/client/"></script>
</head>
<body>
<!-- Yoti element will be rendered inside this DOM node -->
<div id="xxx"></div>
<!-- This script snippet will also be required in your HTML body -->
<script>
window.Yoti.Share.init({
elements: [
{
domId: "xxx",
scenarioId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
clientSdkId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
type: "inline",
displayLearnMoreLink: true,
button: {
label: " Use Yoti",
title: " Scan with the Yoti app"
}
}
]
});
</script>
</body>
Instant QR
The instant QR code option is just the QR code with no button. You will need to provide more context as to what Yoti is for this. Example below:

Instant QR code
<!-- Simple Button Generation -->
<head>
<script src="https://www.yoti.com/share/client/"></script>
</head>
<body>
<!-- Yoti element will be rendered inside this DOM node -->
<div id="xxx"></div>
<!-- This script snippet will also be required in your HTML body -->
<script>
window.Yoti.Share.init({
elements: [
{
domId: "xxx",
scenarioId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
clientSdkId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
type: "inline",
displayLearnMoreLink: true,
qr: {
title: " Scan with the Yoti app"
}
}
]
});
</script>
</body>
Yoti QR code page
This is automatically generated when you create an application in the hub. You will define the following inside the hub.
- Attributes
- Call back URL
- Customisation (logo and background)
This service is a no code integration see Set up for more information, you can be live in a matter of minutes.
Please see this page as an example.
Static QR
A static QR code has no expiry to reload the QR code. Dynamic QR codes have an expiry of 3 minutes until a reload is required. This provides a low cost/offline verification that users can print out and use as many times as needed. Data sent via a static QR code can be viewed in the hub as a receipt.
The process is:
- Create an organisation on the hub.
- Create an application on the hub and add your scenario.
- Send your SDK ID to clientsupport@yoti.com.
We aim to get your QR code to you in 3-4 working days.
Non Browser QR
If you're looking at integrating Yoti with a 'Non-browser' client, one of the tasks you will need to do is request a Yoti QR code from Yoti's servers. This URL must be transformed into a QR code before it can be scanned with the Yoti app. Yoti provides a simple API to transform this URL into an official Yoti QR.
This section below describes the process of creating a personalised client which triggers the Yoti QR flow.
It is possible to manually construct this QR code as this can be necessary in cases where you do not have access to a Web server on your front end, for example using a kiosk of GUI.
Requesting a QR code is broken down into the following steps:
- Requesting a QR code from Yoti Servers
- Rending the QR code
- Decoding and resolving the QR code metadata
- Opening a web socket channel to Yoti using the resolved metadata
- Receiving a Yoti token over the web socket channel or handling a session error
- Passing the received token to the callback url that has been specified in Yoti Hub
Request a QR code
In order to request a QR code from Yoti Servers the client must create a GET HTTPS
request to the following Retrieve a QR Code
endpoint:
Retrieve a QR code endpoint |
---|
GET https://api.yoti.com/api/v1.1/sessions/apps/<sdkId>/scenarios/<scenarioId> |
Parameter | Description |
---|---|
sdkId | sdkId is the SDK_ID found with your Yoti Hub Application |
scenarioId | scenarioId is the Scenario ID found within your Yoti Hub Application that relates to a specific scenario |
A Yoti QR code contains a URL and is returned in full from the Retrieve a QR code
endpoint.
An example of how a URL in a QR code should look is as follows:
https://code.yoti.com/CAEaJDlhMzg5MTRiLTg0ZDAtNGI3Zi05NDNiLWQzNzBmNmM3YTVlYzAA
Rendering a Yoti QR Code
This URL must be transformed into a QR code before it can be scanned with the Yoti app. Yoti provides a simple API to transform this URL into an official Yoti QR - This is important to establish trust between your user base and Yoti.

Static QR code example
POST https://api.yoti.com/api/v1/qrcodes/image
The following headers are required to use the image service:
Header | Value | Description |
---|---|---|
Accept | The follow Accept values can be provided in the header
| Returns a formatted QR code as an svg or png |
Content-Type | application/json | Sets the correct content type |
The request body should be formed as JSON, specifying a URL (mandatory) and an image size (option).
{
"url": "https://code.yoti.com/<YotiCode>"
}
Field | Value |
---|---|
url | This is the URL returned from requesting a Yoti QR |
Error codes
Code | Description | Resolution |
---|---|---|
400 | Missing url field in the request body | Ensure to POST a JSON body containing a url |
406 | Empty Accept header | Send a request Accept header with the required image return type |
406 | An Accept header was sent with an invalid return type | Ensure the Accept header is one of
|
Decoding and resolving the QR code metadata
In order to decode and resolve the URL returned by the requesting a QR code
endpoint and retrieve the QR code metadata, it is necessary to:
- Split the URL by
https://code.yoti.com/
keeping the QR code data, i.e.CAEaJDlhMzg5MTRiLTg0ZDAtNGI3Zi05NDNiLWQzNzBmNmM3YTVlYzAA
- Base 64 url decode the obtained string.
byte[] bytes = Base64.getUrlDecoder().decode(str);
Unmarshal the protocol buffer data structure using the following proto definition:
syntax = "proto2";
package qrpubapi_v1;
option java_package = "com.yoti.qrpubapi_v1";
option java_outer_classname = "QrCodeMsgProto";
enum Issuer {
CONNECT = 1;
}
enum Transport {
UNDEFINED = 0;
URI = 1;
QR_CODE = 2;
}
message QrCodeMsg{
optional Issuer issuer = 1;
optional bytes sharing_token = 2;
optional bytes ref_id = 3;
optional bytes policy = 4;
optional Transport transport = 6;
}
QrCodeMsg qrCodeMsg = QrCodeMsg.parseFrom(bytes);
Although there are few fields in the described data structure, we are only interested in the field named "ref_id", since the others are used by the Yoti app, in order to properly handle the scanned code.
The ref_id field must be interpreted as a UTF-8 string (default encoding for the Java language when creating a String object) and used to retrieve the QR code meta data. This can be achieved by invoking the retrieve QR code metadata endpoint:
https://api.yoti.com/api/v1.1/qrcodes/refs/<ref_id>
Parameter | Description |
---|---|
ref_id | This is the UTF-8 string representation of the ref_id field obtained from the proto buffer data structure. |
The result of this invocation is a JSON payload that looks like the following:
{
"name": "App name",
"policy": "CggKBnNlbGZpZQoOCgxwaG9uZV9udW1iZXISEgoQYXBwbGljYXRpb25fbG9nbxIRCg9hcHBsaWNhdGlvbl91cmwSEgoQYXBwbGljYXRpb25fbmFtZRIdChthcHBsaWNhdGlvbl9yZWNlaXB0X2JnY29sb3IYASAA",
"application_id": "a9c4a5ad-d9e5-4fc8-a561-6eedbc882691",
"application_name": "Yoti App",
"receipt_bg_color": "#2D9FFF",
"callback_endpoint": "https://yoti.com/app",
"sharing_token": "2z94Ix7B7t6B+C3aUSy/6bBuMzQJYZqEBbQa9fp3O6r0bqRgpp0VveojLu8FxdLT",
"session_data": "0LBGRyLy-422ef9b5-871f-4fba-8bed-e70bc4a1e650"
}
The field we are interested in is the one named "session_data" and it will be used to establish a web socket channel through which our client will get notified about a successful/failed interaction.
Attribute "callback_endpoint" will be used in the last step of this guide.
Opening a web socket channel
Opening a web socket channel to Yoti using the resolved metadata
To create a web socket channel to Yoti servers, it is necessary to hit the following web socket channel
endpoint using a web socket client:
wss://api.yoti.com/api/v1.1/connect-sessions/<session_data>
Parameter | Description |
---|---|
session_data | The session_data obtained from the call to retrieve QR code endpoint |
Once the connection is established, you will need to send an initial subscription message, the value of this is the session_data used to create the channel. An example of this is shown below.
{
"subscription": <session_data>
}
var socket = new WebSocket(socketAddress + '/connect-sessions/' + session_data)
socket.onopen = function() {
socket.send(JSON.stringify({subscription: session_data}))
}
The server will send the following message back over the channel:
//successful message
{
"status" : "ACCEPTED",
"subscription" : "0LBGRyLy-422ef9b5-871f-4fba-8bed-e70bc4a1e650"
}
// invalid session_data value has been provided,
{
"status" : "REJECTED",
"subscription" : "0LBGRyLy-422ef9b5-871f-4fba-8bed-e70bc4a1e650"
}
Receiving a token
Receiving a Yoti token over the web socket channel or handling a session error. Assuming the connection was successfully established, a further message will be prompted that signals the completion of the transaction.
If the transaction fails, the server will send the following message:
{
"status" : "FAILED",
"subscription" : "0LBGRyLy-422ef9b5-871f-4fba-8bed-e70bc4a1e650"
}
If the transaction times out, the server will send the following message:
{
"status" : "TIMED_OUT",
"subscription" : "0LBGRyLy-422ef9b5-871f-4fba-8bed-e70bc4a1e650"
}
If any of the last two messages occur, you should restart the process by requesting a new QR code.
Let's finally see what happens when a transaction is successfully completed. In this case the server will send the following message:
{
"status" : "COMPLETED",
"subscription" : "0LBGRyLy-422ef9b5-871f-4fba-8bed-e70bc4a1e650",
"token" : "egouerghwl4itjxpri4oty5ihfli4yg7tgbnkrliu"
}
The token here is the Yoti token that must be passed to your application back-end, in order to retrieve the user shared profile using the Yoti SDKs.
Below is a javascript example that shows how to implement the logic that is necessary to correctly handle the channel communication.
var socket = new WebSocket(socketAddress + '/connect-sessions/' + connection)
socket.onopen = function() {
socket.send(JSON.stringify({subscription: connection}))
}
socket.onmessage = function(e) {
var message = JSON.parse(e.data)
switch(message.status) {
case 'COMPLETED':
var token = message.token
// send token to backend here
break;
case 'ACCEPTED':
break;
case 'TIMED_OUT':
// restart process
break;
case 'REJECTED':
// restart process
break;
}
}
Passing the token to the callback URL
Passing the received token to the callback url that has been specified in Yoti Hub.
Finally, the received token must be forwarded to your back-end, which should be listening for GET requests at the address specified by the attribute named "callback_endpoint" that has been returned as a response to the request to requesting a QR code
endpoint.
The token should be provided as a query string parameter, so, in this example, the client should send a GET request to the follow URL:
https://yoti.com/app?token=egouerghwl4itjxpri4oty5ihfli4yg7tgbnkrliu
It is worth noticing that, in case you are deploying your client in different physical locations, this would be the best point to provide some metadata by personalising your request to your own back-end.
The example above could be extended by providing more parameters:
https://yoti.com/app?token=egouerghwl4itjxpri4oty5ihfli4yg7tgbnkrliu&lane=2&location=WC2N4JH
Your endpoint can return any value in any format, since your client will handle it. Our suggestion is at least to capture the result of the interaction with the Yoti SDK and tell the client whether the interaction succeeded or not.
Dynamic QR
This QR code is generated differently. By "dynamic" we mean that the attributes must be provided as part of the QR code request as opposed to where the attributes is defined upfront in hub. This allows for higher flexibility, for example as part of your integration you can create a set of fields that must be filled in by the user, the set of required attributes is intrinsically dynamic from Yoti as it depends on the type of information received manually.
Please launch this demo to view how this works: https://yoti.world/dynamicqr/.
This section below describes the process of creating a dynamic QR code broken down into the following steps:
- Creating the policy
- Creating the scenario
- Creating a share URL
Creating the policy
A policy is used to define what attributes are requested from the user. You can use the builder to define what attributes are needed. There are multiple different ways to add an attribute to a policy:
1) Using our self built method allowing you to customise the attribute with source constraints.
2) Using our out of the box attribute with default settings, which is no constraints.
3) Easier readable methods with default settings, which is no constraints.
const selfBuiltAttribute = new Yoti.WantedAttributeBuilder()
.withName("full_name")
.withConstraints(constraints)
.withAcceptSelfAsserted()
.build();
const dynamicPolicy = new Yoti.DynamicPolicyBuilder()
//using an attribute you build
.withWantedAttribute(selfBuiltAttribute)
//using an attribute by name
.withWantedAttributeByName("full_name")
//using a predefined method to add an attribute
.withFullName()
//if you wish the user to prove it's them by taking a selfie on share
.withSelfieAuthentication()
.build();
You can apply constraints to an attribute. The only available constraint is to limit the source of the attribute, eg. to limit full_name to only come from a passport.
If a constraint has "soft preference" set to 'true' then the attribute will still be shared from another source if it the specified source is not present.
See Attributes explained section for more details.
const sourceConstraint = new Yoti.SourceConstraintBuilder()
//passport
.withPassport()
//Driving Licence
.withDrivingLicence()
//National ID
.withNationalId()
//Passcard
.withPasscard()
.withSoftPreference(false)
.build();
const constraints = new Yoti.ConstraintsBuilder()
.withSourceConstraint(sourceConstraint)
.build();
const selfBuiltAttribute = new Yoti.WantedAttributeBuilder()
.withName("full_name")
.withConstraints(constraints)
.withAcceptSelfAsserted()
.build();
Creating a scenario
The Dynamic Scenario is built using:
- The policy
- A callback URL for share completion. After the share is completed, the callback URL is where the user will be redirected to the specified page. A one time use token will be added to the URL. You can use this token to retrieve the profile of the share.
- Any extensions e.g. Location constraint
const dynamicScenario = new Yoti.DynamicScenarioBuilder()
.withCallbackEndpoint("/your-callback")
.withPolicy(dynamicPolicy)
//if using an extension
.withExtension(extension)
.build();
Create a share URL
Using the dynamic scenario you need to get a shareURL which will be used by the Yoti scripts to generate a Yoti QR code.
yotiClient.createShareUrl(dynamicScenario)
.then((shareUrlResult) => {
const shareURL = shareUrlResult.getShareUrl();
}).catch((error) => {
console.error(error.message);
});
Once you have your share URL you can send it to the frontend for it to be rendered in a Yoti QR code. Please see example below using the modal QR code.
<!-- Simple Button Generation -->
<head>
<script src="https://www.yoti.com/share/client/"></script>
</head>
<body>
<!-- Yoti element will be rendered inside this DOM node -->
<div id="xxx"></div>
<!-- This script snippet will also be required in your HTML body -->
<script>
window.Yoti.Share.init({
elements: [
{
domId: "xxx",
shareUrl: "shareURL",
clientSdkId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
displayLearnMoreLink: true,
}
]
});
</script>
</body>
Examples
Full examples are located here:
Query parameters
You can append query params to the landing page URL that displays the Yoti button. These will be added to the callback URL.
For example if you load the landing page containing the Yoti button as follows:
https://example.com/?iso=test&user_id=6667
The query parameters (iso=test&user_id=6667) will be returned in the callback URL.