NAV Navbar
cURL javascript python

Get Started

Welcome to Ubble's API documentation !

In the dashboard, in the developper space, you will be able to generate a CLIENT_ID and a CLIENT_SECRET. Make sure that you have created them, as they will be used across all the documentation.

By the end of this section, you will be able to create an identification, check your identity and consult results on Ubble's identity verification service.

Create an identification

Create an identification

curl -X POST 'https://api.ubble.ai/identifications/' \
  -u CLIENT_ID:CLIENT_SECRET | jq
const axios = require("axios");
const config = {
  auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
  headers: {
    Accept: "application/vnd.api+json",
    "Content-Type": "application/vnd.api+json",
  },
};
axios
  .post("https://api.ubble.ai/identifications/", null, config)
  .then((res) => {
    const identification = res.data;
    const attributes = identification.data.attributes;
    const identificationId = attributes["identification-id"];
    console.log(attributes["identification-url"]);
    console.log(attributes["redirect-url"]);
  })
  .catch((err) => console.log(err.response.data));
import requests

headers = {
  "Accept": "application/vnd.api+json",
  "Content-Type": "application/vnd.api+json"
}

res = requests.post(
    "https://api.ubble.ai/identifications/",
    auth=("CLIENT_ID", "CLIENT_SECRET")
    headers=headers,
)
identification = res.json()
attributes = identification["data"]["attributes"]
identification_id = attributes["identification-id"]
print(attributes["identification-url"])
print(attributes["redirect-url"])

To verify the identity of a user you will need to create an identification object. The identification object is the main object of Ubble's product and corresponds to one identity verification, see objects reference. One identification corresponds to one and only one user.

Verify your identity

Go to the identification-url from the identification object that you've just created and complete Ubble's identity verification process (at the end, you will be redirected to the redirect_url associated to the identification, see configuration to customize it).

As you will see if you use a desktop you will be redirected to your phone to perform the verification. You will be able to prefill the phone_number when generating an identification in production. See create an identification.

Get the results

Get the results

curl 'https://api.ubble.ai/identifications/:identification-id/' \
  -u CLIENT_ID:CLIENT_SECRET | jq
const axios = require("axios");
const config = {
  auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
  headers: {
    Accept: "application/vnd.api+json",
  },
};
axios
  .get(`https://api.ubble.ai/identifications/${identificationId}/`, config)
  .then((res) => {
    const identification = res.data;
    console.log(identification.data.attributes.score);
    // Getting first name, last name and birth date
    console.log(
      identification.included.find((obj) => obj.type === "identities")
        .attributes
    );
  })
  .catch((err) => console.log(err.response.data));
import requests

headers = {"Accept": "application/vnd.api+json"}

res = requests.get(
    f"https://api.ubble.ai/identifications/{identification_id}/",
    auth=("CLIENT_ID", "CLIENT_SECRET"),
    headers=headers,
)
identification = res.json()
print(identification["data"]["attributes"]["score"])
for obj in identification["included"]:
    # Getting first name, last name and birth date
    if obj["type"] == "identities":
        print(obj["attributes"])

Once you're done, you can fetch the results.
Congrats ! You've completed your first identification. To go further, you can go through the different steps of our integration, or look at the results that you can get from an identification.

Integration

To integrate with Ubble identity verification service, you will need to follow these five steps:

  1. Create identification object in your backend for a user
  2. Redirect user to the identification-url in your frontend
  3. Manage user return
  4. Get automatic answer
  5. Get final answer

Integration flow

Step 1: Create an identification object

At this point you should have created your first identification with our Get started. We strongly advise you to create your identification in your backend, to avoid using your Ubble credentials in your frontend. Once your backend is correctly setup to create identification, you can go to Step 2.

All the details to create an identification can be found there: Create identification - POST /identifications/

The external_user_id is mandatory in the POST request (in the identification-form object).

You can modify the redirect_url, or use the one that is set by default in your configuration, see how.

We strongly recommend you to send the phone_number if you use a desktop flow : we use it to pre-fill the SMS redirection page (one of the 2 redirection options along with the QR Code).

The identity verification process has an asynchronous response (since it's dependent on the user pace and Ubble's processing). To be notified of every identification status change, you can configure a webhook, see how. You can modify the webhook url.

Step 2: Redirect user

Once you have created an identification object your user needs to be redirected to Ubble's web application as explained on the graph below.

Integration flow

Ubble uses RTCPeerConnection and getUserMedia to enable the live streaming video on the web. Therefore we depend on the different implementations of web browsers.

Web Redirection

You can redirect user using web redirection. We support all the major browsers current versions.

We've built an integration example showcasing how to integrate Ubble in a web app:

Web Redirection is compatible with the following browsers / OS. We do not ensure the compatibility of our service with beta versions of browsers / OS.

Desktop Chrome Firefox Safari Internet Explorer Edge Opera
Min. Version ≥ 53 ≥ 52 ≥ 11.1 - ≥ 16 ≥ 58
Android Chrome for Android Firefox for Android Samsung Internet IE Mobile
Min. Version ≥ 73 ≥ 66 ≥ 6.2 -
iOS Safari Mobile Chrome All other browsers
Min. Version ≥ 11.0 ≥ 14.3 -

To integrate Ubble in your website, create an identification in your backend, pass the identificationUrl to your web app and simply add it as a link :

<a href="`${identificationUrl}`"></a>

Webview integration

For a more integrated user experience in your mobile application, you can redirect your user using a webview.

Here is the list of integration examples that we've built for you :

For iOS Native you will have to open safari using the open method:

UIApplication.shared.open(identificationUrl, options: [:])


Flutter

If you wish to use Ubble with Flutter, you can use the flutter_inappwebview.

Make sure to follow the WebRTC API section of the documentation to ensure we're able to launch a video stream.

Iframe integration

When using the Desktop-to-Mobile redirection flow, we recommend that you use our Iframe integration on desktop described below:

Include the SDK as a script tag.

If you need an example of Iframe usage, you can use our iframe tester

<script
  src="https://cdn.ubble.ai/iframe-sdk-0.0.3.js"
  type="application/javascript"
></script>

We expect your page to implement a global method onUbbleReady that will be triggered when the SDK is loaded

function onUbbleReady() {}


Initialize a new Ubble IDV object:

function onUbbleReady() {
  const ubbleIDV = new Ubble.IDV(document.getElementById("ubble"), options);
}



API

elementId: string | Element
new Ubble.IDV("ubble", options);
new Ubble.IDV(document.getElementById("ubble"), options);
options Object
{
  "status": "processing",
  "redirectUrl": "https://your-redirect-url.com?identification_id=<ubble identification id>"
}
{
  "status": "aborted",
  "returnReason": "NO_DOCUMENT",
  "redirectUrl": "https://your-redirect-url.com?identification_id=<ubble identification id>"
}
{
  "status": "expired",
  "redirectUrl": "https://your-redirect-url.com?identification_id=<ubble identification id>"`
}



Methods

destroy()
const ubbleIDV = new Ubble.IDV("id", {
  events: {
    onComplete(event) {
      console.log(`Done with ubble flow: ${event}`);
      ubbleIDV.destroy();
    },
  },
});



Full example

const ubbleIDV = new Ubble.IDV("idv", {
  width: "500",
  height: "600",
  allowCamera: true,
  identificationUrl: "https://id.ubble.ai/11111111-1111-1111-1111-111111111111",
  events: {
    onComplete(event) {
      ubbleIDV.destroy();
    },
    onAbort(event) {
      ubbleIDV.destroy();
    },
    onExpired(event) {
      ubbleIDV.destroy();
    }
  },
});



Known Issues

Step 3: Manage user return

When users have completed the verification, they are redirected to the redirect_url associated with the identification.

You can customize this redirect_url, see configuration.

Users also have the option of leaving the verification, either voluntarily or because they have encountered an error, using the calls to action in the header of the application. In this case, they are also redirected to the redirect_url, but the status is aborted and you find the reason for their abandonment in the URL parameter.

For example :

?identification_id=123&status=aborted&return_reason=error&error_type=device_not_found

Possible values are

status return_reason error_type description
processing none none The user completed his verification.
aborted refusal none The user refused to perform the verification now.
aborted no_document none The user did not have his document with him.
aborted verify_later none The user refused to start the verification process.
aborted error bad-connectivity The connexion was not good enough.
aborted error publish Error during video connection.
aborted error browser-not-supported The browser was not supported.
aborted error device-not-allowed The user refuses to give access to the camera.
aborted error device-not-found The user’s device did not have any camera.
aborted error wrong_phone_number The user wanted to change their phone number and were not allowed to for security reasons (see Configuration).

Step 4: Get automatic answer

The automatic answer is already available when the user is redirected to the redirect_url.

If you configured a webhook, see how, you will be notified as the status switch from initiated to processing.

Getting this answer is similar to what you did in the Get started.

The objects available in the automatic answer are identification, identity, reference-data and reference-data-check. If you choose the form option, see forms, the objects form and identification-form-match will also be available. Assets are included in document and face objects. See see objects reference.

Step 5: Get final answer

The final answer is available at the end of the manual reviews. This delay is defined in the service-level agreements (SLAs).

If you configured a webhook, see how, you will be notified as the status switch from processing to processed.

Getting this answer is similar to what you did in the Get started.

All objects are now available. To better understand the different results see identification results.

Identification results

Objects

All the objects mentionned below are accessible via the GET /identifications/:identification-id endpoint. In depth description is available in API Reference.

Name Description
identification Main object. Stores technical infos of the identification, includes other objects
identity Object summarizing the identity of the user
reference-data Identity sent when the identification is created
reference-data-check Match between reference data and Identity
document Object storing all the data extracted from the user's document
document-check Security checks of a document object
face Object storing all the data extracted from the user's face
face-check Security checks of a face object
doc-face-match Face match check between a document and a face object
doc-doc-match Match check between two documents
form Identity declared by the user in the ubble pre-filled form (optional)
identity-form-match Match check between identity and form (optional)
reason-code Codes that provide insight on why an identification was not validated

Lifecycle

Identification has a status field that can take different values during its life.
Depending on the status, the GET identification call includes different objects:

Status Description Included objects
uninitiated Identification has only been created (user has not started the verification flow) identification, identity, reference-data
initiated User has started the verification flow identification, identity, reference-data
processing User has ended the verification flow, identification-url is not usable anymore identification, identity, form, identity-form-match, reference-data, document, face
processed Identification is completely processed by Ubble identification, identity, form, document, document-check, face, face-check, doc-face-match, doc-doc-match, identity-form-match, reference-data, reference-data-check
aborted User has left the identification, the identification-url is no longer usable identification, identity, reference-data
expired The identification-url has expired and is no longer usable (only uninitiated and initiated identifications can become expired)

Scores

Identification has a score field that can take different values. All the scores in the different objects follow the same rule:

Score Description
1.0 The check is validated
0.0 The check is invalidated. Ubble has enough information to say that there is an issue (e.g. document is expired)
-1.0 The check cannot be validated. Ubble does not have enough information to provide a correct answer (e.g. user did not show its ID card during the video)

Reason codes

Identification has a relationship named reason-codes.

In case an identification is not validated, reason codes can provide some insight on why it was the case. A refused identification can contain multiple reason codes.

Code Description
1201 Applicant did not have a sufficient connexion
1301 Applicant’s document video is too blurry (mostly due to too much movement but if this error persists the camera quality might be at fault)
1302 Applicant has not presented the front of the document
1303 Applicant has not presented the back of the document
1304 Applicant hides part of the document
1305 Applicant did not present a dynamic view of the document
1310 Applicant’s video of their face is too blurry (mostly due to too much movement but if this error persists the camera quality might be at fault)
1311 Applicant has not presented a face
1312 Applicant did not show the full front view of their face
1313 Applicant did not move to prove the liveness
1320 Applicant performed their id verification under poor lighting conditions
1901 Internal error
1911 The received videos cannot be played
2101 Applicant presented an expired document
2102 Applicant presented a document which is not accepted
2103 Applicant has submitted a damaged document
2201 Applicant presented a photocopy of the document
2202 Applicant presented the document on a screen
2301 Applicant has submitted a counterfeit or falsification
2302 Applicant presented a document declared as stolen or lost
2303 Applicant presented the front and back of two different documents
2304 Applicant is not the rightful owner of the document
2305 Applicant presented another person's face
2306 Applicant has altered his/her appearance
2307 Applicant has digitally altered the videos
2401 Applicant’s identity does not match with the expected one
2402 Applicant used a device that has been technically altered
2403 Applicant seems to have performed the check against his will

Comment

Identification has a comment field providing insights about processed identifications that are not validated.

The comment field combines different sub-comments, each of them being generated by different events.

The different values of these subcomments are detailled below.

The comment field is mostly filled automatically depending on the context, however it can also be expanded manually by reviewers if necessary.

The subcomments used to generate the comment field can take the following values:

Comment
First document {media_type} resolution is too low.
First document is not presented.
First document {media_type} quality is too low.
First document is not supported.
Back of first document is not presented.
Back of first document {media_type} quality is too low.
Some required data of first document could not be extracted.
Face is not presented.
Face {media_type} quality is too low.
Authenticity check of first document has failed.
The first document is expired.
Authenticity of first document could not be verified.
User is not live.
User liveness could not be verified.
Face and first document photo do not match.
Face and first document photo match could not be verified.
The face does not match the reference face.
Face and reference face match could not be verified.
The document is unknown or known to be stolen or lost by the authorities.
Extracted identity and reference data do not match.
The device used by the user has been modified.
The face or document is known to be fraudulent.
Extracted identity and form do not match.
Second document is not presented.
Second document {media_type} quality is too low.
Second document is not supported.
Back of second document is not presented.
Back of second document {media_type} quality is too low.
Some required data of second document could not be extracted.
Authenticity check of second document has failed.
The second document is expired.
Authenticity of second document could not be verified.
Face and second document photo do not match.
Face and second document photo match could not be verified.
Some data between the two documents do not match.

where:

PDF

Get PDF result

curl -X POST 'https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/pdf/' \
 -u CLIENT_ID:CLIENT_SECRET > identification.pdf
const axios = require("axios");
const config = {
  auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
  responseType: "stream",
};
axios
  .post(`https://api.ubble.ai/identifications/${identificationId}/pdf/`, config)
  .then(function (response) {
    response.data.pipe(fs.createWriteStream("~/identification.pdf"));
  })
  .catch((err) => console.log(err.response.data));
import requests
headers = {
    "Accept": "application/vnd.api+json",
}
identification_id = "11111111-1111-1111-1111-111111111111"

res = requests.post(
    f"https://api.ubble.ai/identifications/{identification_id}/pdf/",
    auth=("CLIENT_ID", "CLIENT_SECRET"),
)
if res.ok:
    open("identification.pdf", "wb").write(res.content)
else:
    print("Error", res.content)

Once an identification is processed, you can request a pdf export of an identification by calling the endpoint.

POST - /api/identifications/:identification_id/pdf/

Internationalization

You can request the PDF in different languages, by either adding a lang query parameter, or sending an Accept-language header

POST - /api/identifications/:identification_id/pdf/?lang=en

curl -X POST 'https://id.ubble.ai/api/identifications/:identification_id/pdf/' --header 'Accept-Language: fr-FR'

Supported languages are currently English and French, the default language is English.

Examples

Get standard answer

curl 'https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/' \
  -u CLIENT_ID:CLIENT_SECRET | jq
const axios = require("axios");
const identificationId = "11111111-1111-1111-1111-111111111111";
const config = {
  auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
  headers: {
    Accept: "application/vnd.api+json",
  },
};
axios
  .get(`https://api.ubble.ai/identifications/${identificationId}/`, config)
  .then((res) => {
    const identification = res.data;
    console.log(
      `Identification score is ${identification.data.attributes.score}`
    );
    identification.included.forEach((obj) => {
      console.log(`Object ${obj.type} :`);
      console.log(obj.attributes);
    });
  })
  .catch((err) => console.log(err.response.data));
import requests

headers = {
    "Accept": "application/vnd.api+json",
}
identification_id = "11111111-1111-1111-1111-111111111111"

res = requests.get(
    f"https://api.ubble.ai/identifications/{identification_id}/",
    auth=("CLIENT_ID", "CLIENT_SECRET"),
)
identification = res.json()
print(f'Identification score is {identification["data"]["attributes"]["score"]}')
for obj in identification["included"]:
    print(f'Object {obj["type"]} - {obj["attributes"]}')

In order to ease your integration, you are provided with 3 standard answers that you can play with.

identification-id score description
11111111-1111-1111-1111-111111111111 1.0 The identification is validated
00000000-0000-0000-0000-000000000000 0.0 The identification is not validated because the document is a photocopy
22222222-2222-2222-2222-222222222222 -1.0 We're missing some information to make a final decision : in this example, the user didn't show the back of the ID

Configuration

Webhook

Webhook body Processed

{
  "configuration": {
    "id": 5,
    "name": "MyConfig"
  },
  "identification_id": "11111111-1111-1111-1111-111111111111",
  "status": "processed"
}

Webhook body Aborted

{
  "configuration": {
    "id": 5,
    "name": "MyConfig"
  },
  "identification_id": "11111111-1111-1111-1111-111111111111",
  "status": "aborted",
  "return_reason": "no_document"
}

The identity verification process has an asynchronous response (since it depends on the user pace and ubble's processing). By configuring your webhook, you will be notified of every identification status change. You will be notified when the user starts, successfully ends, and when the identification has been processed by Ubble.

You can easily configure your webhook:

We expect that you return us a 200 or 201 status code within 10 seconds before we retry, up to 2 retries will be performed. If needed, webhook notifications can be resend manually using the notify endpoint.

Webhook security

import hashlib
import hmac

# Ubble request is the webhook call
request = ubble_request

ubble_signature = request.headers['Ubble-Signature']
ubble_signature_dict = dict(token.split('=') for token in ubble_signature.split(','))
# Let's compare the hash

# First we create the signed_payload
signed_payload = ubble_signature_dict['ts'] + '.' + request.body

# Then we create the hash
expected_signature = hmac.new(
  # WEBHOOK_SECRET can be found in the Configuration page on your dashboard.
  WEBHOOK_SECRET.encode('utf-8'),
  msg=signed_payload.encode('utf-8'),
  digestmod=hashlib.sha256
).hexdigest()

# Then we compare both signature.
expected_signature == ubble_signature_dict['v1']

For security reason all our webhook calls are signed.

Ubble-Signature header contains the timestamp of the signature plus the signature itself. This allow you to ensure the content of the webhook was not modified.

The timestamp is prefixed by ts=, and the signature is prefixed by v1=. Example : ts=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

To compare the signature, you will need the webhook secret, that you can find on your dashboard configuration page. There is one for secret for test identifications and one for live identifications.

See the example in python if you need help.

Webhook ip whitelisting

For security purposes, if you need to whitelist our incoming api calls, see our public ip.

Webhooks Authentication

You can also choose to authentify our webhook calls against OAuth2. Please contact your account manager in order set up the authentication. You will need to provide an URL Token, a CLIENT_ID, a CLIENT_SECRET and optionnally a refresh token URL.

Redirect URL

You can easily configure the redirect-url of your identifications :

Form

You may need to use the user's full identity data in your on-boarding workflow. In this case, to make the automatically extracted data more reliable, we suggest you create a form in the user path.

The form will be pre-filled with the data extracted during the flow, and can contain all the fields of the extended identity : first name, last name, married name, gender, nationality, birth place, country, birth place, city. The results of the form will be available in the object form in the identification results.

Moreover you can get a confirmation of the accuracy of this data in the final results. We compare the extracted name, first name and birthdate with the form data. The results are available in the identity-form-match object.

Other

Many elements are configurable, see with your account manager. You will be able to change the UI of your Ubble flow (colors, logos, and country / document choices). You will also be able to change how the user data is stored on our servers.

API Reference

API public ip

The api is hosted on our cloud provider, whose public ip adress is available on his website.

JSON API

API Host

https://api.ubble.ai/

All request and response bodies, including errors, are encoded in JSON. Our api follows the jsonapi spec for formatting. Examples of response objects can be found in the spec can be found here. For security reasons most endpoints are disabled.

Authentication

The Ubble API uses two types of authentication. For standard accounts Basic authenication is used, and for certified accounts mutual TLS authentication in addition to Basic Authentication is used. Basic authentication credentials and mutual TLS authentication certificates can be managed in the client dashboard or by your account manager.

Basic Authentication

Authentication using HTTP basic auth.

curl -X POST 'https://api.ubble.ai/identifications/' \
  -u {CLIENT_ID}:{CLIENT_SECRET}
import requests

headers = {
  "Accept": "application/vnd.api+json",
  "Content-Type": "application/vnd.api+json"
}

res = requests.post(
    "https://api.ubble.ai/identifications/",
    auth=("CLIENT_ID", "CLIENT_SECRET"),
    headers=headers
)
const axios = require("axios");
const config = {
  auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
  headers: {
    Accept: "application/vnd.api+json",
    "Content-Type": "application/vnd.api+json",
  },
};
axios
  .post("https://api.ubble.ai/identifications/", null, config)
  .then((res) => console.log(res.data))
  .catch((err) => console.log(err.response.data));

You must authenticate your calls by including your secret credentials in API requests. You can manage your credentials with your account manager. Your credentials carry many privileges, so be sure to keep them secure. Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.

Requests are authenticated using HTTP Basic Auth. Provide your CLIENT_ID and CLIENT_SECRET as the Basic Auth username and password.

All API requests must be made over HTTPS as calls made over plain HTTP will fail. API requests without authentication will also fail.

Mutual TLS Authentication

Authentication using mutual TLS and Basic auth.

curl -X POST 'https://api.ubble.ai/identifications/' \
  -u {CLIENT_ID}:{CLIENT_SECRET} --cert /path/client.crt --key /path/client.key
import requests

headers = {
  "Accept": "application/vnd.api+json",
  "Content-Type": "application/vnd.api+json"
}

res = requests.post(
    "https://api.ubble.ai/identifications/",
    auth=("CLIENT_ID", "CLIENT_SECRET"),
    cert=('/path/client.crt', '/path/client.key'),
    headers=headers
)
const axios = require("axios");
const fs = require("fs");
const https = require("https");
const config = {
  auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
  headers: {
    Accept: "application/vnd.api+json",
    "Content-Type": "application/vnd.api+json",
  },
};
const httpsAgent = new https.Agent({
  cert: fs.readFileSync("/path/client.crt"),
  key: fs.readFileSync("/path/client.key"),
});
axios
  .post("https://api.ubble.ai/identifications/", { httpsAgent }, config)
  .then((res) => console.log(res.data))
  .catch((err) => console.log(err.response.data));

You must authenticate your calls by including your secret credentials as well as your client certificate and key in API requests. You can manage your credentials and certificates with your account manager. Your credentials and certificates carry many privileges, so be sure to keep them secure. Do not share your secret API keys or API certificates in publicly accessible areas such as GitHub, client-side code, and so forth.

Requests are authenticated using HTTP Basic Auth and/or mutual TLS Provide your CLIENT_ID and CLIENT_SECRET as the Basic Auth username and password. Provide your client certificate and client key as the mutual TLS certificate authentication

All API requests must be made over HTTPS as calls made over plain HTTP will fail. API requests without authentication will also fail.

API response signing

If enabled in your configuration (Contact your account manager in order to activate the response signature), Ubble API responses will be signed. This signature can be used to verify that the api response has not been altered and has been issued by Ubble and not by anyone else. To also verify that assets data (images) has not been altered and comes from Ubble, the assets hash has been added to the response body, and after verification of the signature, the images can be hashed and compared to the hashes in the verified response. Ubble uses an asymetric key signing algorithm of type ECDSA with a sha2-512 hashing algorithm. Assets are hashed using the sha2-256 hashing algorithm.

Signing key policy

A signing key will be created for each configuration and for each environment (Live and Test). The public key (used for signature verification) associated with the private key that is managed by ubble can be downloaded from the client dashboard. The private key has a validity of 1 year and will be automatically renewed. Upon renewal the public key will also change and has to be re-downloaded from the dashboard. You can detect a key rotation by looking at the signature version in the api response, to verify that response you will need to use the public key with the same version. All versions of the public key can be downloaded from the dashboard so you don't have to worry about losing older versions.

Verifying Response signature

import base64
import ecdsa
from ecdsa.util import sigdecode_der
from hashlib import sha512

# response is the python requests response object received from Ubble

# Extract the signature
timestamp, ubble_signature_version, ubble_signature = response.headers["ubble-signature"].split(":")

# First we create the signed_payload
signed_payload = f"{timestamp}:{response.text}".encode("utf-8")

# Use the public key from the dashboard (check version from ubble_signature_version)
public_key_file = open("ubble_public_key.key")

# Generate veryfing key signature
verifying_key = ecdsa.VerifyingKey.from_pem(public_key_file.read())

# Verify the signature
result = verifying_key.verify(base64.b64decode(ubble_signature.encode("utf-8")), signed_payload, hashfunc=sha512, sigdecode=sigdecode_der, allow_truncate=True)
print(result)

The Ubble-Signature header contains the timestamp of the request (1635236316.377888), the signing key identifier and version (3456-live-v1) as well as the signature itself (5257a869a7ecebeda35affa62cdcb3fa51cad7e77a0e56ff546d0ae8e108d8bd).

Example : 1635236316.377888:3456-live-v1:5257a869a7ecebeda35affa62cdcb3fa51cad7e77a0e56ff546d0ae8e108d8bd <timestamp>:<configuration id>-<test or live identification>-<key version>:<signature>

The signature can be verified on your side by verifying the signature against the received body using the public key corresponding to the signature key. You will need the Public key corresponding to the signature version, that you can find on your dashboard configuration page. There is one for test identifications and one for live identifications.

See the example in python if you need help.

Verifying Assets

from hashlib import sha256
import requests

# Download image to verify (example url)
image_url = "https://storage.ubble.ai/image-url.png"
received_image = requests.get(image_url).content

# Hash the image
received_image_hash = sha256(received_image).hexdigest()

# Get the hash from the previously verified response (example)
ubble_response_image_hash = "sha2-256:<image-hash-string>"

# Compare the hashes
result = received_image_hash == ubble_response_image_hash.split(":")[1]
print(result)

To verify that the assets that correspond to the received response are correct, the received images have to be hashed using a specific algorithm, this hash must be the same as the hash that was sent with the signed response.

Be aware that all two verification stages must be valid (api response signature and assets hash) for the data to be valid.

Objects

All the following objects related to the identification are included in the GET identification call once the identification is in processed. Identity and Identification data are always included in the GET call. Note that the score of an identification is only available when the identification is processed.

Identifications

identification object

{
  "type": "identifications",
  "id": "801",
  "attributes": {
    "comment": "Identity Verified",
    "created-at": "2019-02-26T16:29:19.857313Z",
    "ended-at": "2019-02-26T17:05:49.096401Z",
    "identification-id": "70f01f19-6ec5-4b14-9b30-2c493e49df15",
    "identification-url": "https://id.ubble.ai/70f01f19-6ec5-4b14-9b30-2c493e49df15",
    "number-of-attempts": 1,
    "redirect-url": "https://www.ubble.ai/",
    "score": 1.0,
    "started-at": "2019-02-26T16:29:43.075181Z",
    "status": "processed",
    "updated-at": "2019-02-26T17:12:02.226018Z",
    "status-updated-at": "2019-02-26T17:12:02.226018Z",
    "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1",
    "user-ip-address": "123.123.123.123",
    "webhook": "https://3eb5c250.ngrok.io"
  },
  "relationships": {
    "doc-face-matches": {
      "data": [
        {
          "type": "doc-face-matches",
          "id": "546"
        }
      ],
      "links": {
        "related": "http://api/api/identifications/801/doc_face_matches/"
      },
      "meta": {
        "count": 1
      }
    },
    "doc-doc-matches": {
      "data": [
        {
          "type": "doc-doc-matches",
          "id": "62"
        }
      ],
      "links": {
        "related": "http://api/api/identifications/801/doc_doc_matches/"
      },
      "meta": {
        "count": 1
      }
    },
    "reason-codes" : {
      "data": [
        {
          "type": "reason-codes",
          "id": "2401"
        }
      ],
      "meta": {
        "count": 1
      }
    }
    "document-checks": {
      "data": [
        {
          "type": "document-checks",
          "id": "546"
        }
      ],
      "links": {
        "related": "http://api/api/identifications/801/document_checks/"
      },
      "meta": {
        "count": 1
      }
    },
    "face-checks": {
      "data": [
        {
          "type": "face-checks",
          "id": "546"
        }
      ],
      "links": {
        "related": "http://api/api/identifications/801/face_checks/"
      },
      "meta": {
        "count": 1
      }
    },
    "identity-form-match": {
      "data": [
        {
          "type": "identity-form-matches",
          "id": "646"
        }
      ],
      "links": {
        "related": "http://api/api/identifications/801/identity-form-matches/"
      },
      "meta": {
        "count": 1
      }
    },
    "identity": {
      "data": {
        "type": "identities",
        "id": "661"
      },
      "links": {
        "related": "http://api/api/identifications/801/identity/"
      }
    }
  }
}

The identification object contains the information related to the identification performed by the user. This object is included in the response, whatever the status. Depending on the identification's status, some fields will have a NULL value.

Attribute Type Description
comment string Optional text field inputed by the manual reviewer
created-at string (format: YYYY-MM-DDThh:mm:ss.ffffffZ) The identification creation time, when your backend calls ubble’s API to create the identification
ended-at string (format: YYYY-MM-DDThh:mm:ss.ffffffZ) The date and time when the identification final answer is available, when the status changes to processed
identification-id string (UUID) Unique identifier of the identification. It will be used as argument for other API calls
identification-url string Url where the user should be redirected to perform the identification. It follows this format: https://id.ubble.ai/<identification-id>/
number-of-attempts number Number of attempts, or sessions, related to this identification
redirect-url string Url where the user is redirected at the end of the identification
score number Overall trust score of the identification.
  • 1.0: Identification validated
  • 0.0: Identification has issues
  • -1.0: Identification cannot be processed
started-at string (format: YYYY-MM-DDThh:mm:ss.ffffffZ) The date and time when the user made the first attempt / session with the identification
status string Status of the identification, one of: uninitiated, intiated, processing, processed or aborted
updated-at string (format: YYYY-MM-DDThh:mm:ss.ffffffZ) The date and time of the last update to the identification object
status-updated-at string (format: YYYY-MM-DDThh:mm:ss.ffffffZ) The date and time of the last status update to the identification object
user-agent string User agent used for the last session
user-ip-address string IP address from where the user performed the identification
webhook string Url where status change notifications will be sent

Reason codes

{
  "type": "reason-codes",
  "id": "2430"
}

In case an identification is not validated, reason codes can provide some insight on why it was the case. A refused identification can contain multiple reason codes.

The list of reason codes can be found here.

Identities

identity object

{
  "type": "identities",
  "id": "661",
  "attributes": {
    "birth-date": "1978-11-25",
    "first-name": "JEAN",
    "last-name": "VALJEAN"
  },
  "relationships": {
    "document": {
      "data": {
        "type": "documents",
        "id": "632"
      },
      "links": {
        "related": "http://api/api/identities/661/document"
      }
    },
    "face": {
      "data": {
        "type": "faces",
        "id": "629"
      },
      "links": {
        "related": "http://api/api/identities/661/face"
      }
    },
    "form": {
      "data": {
        "type": "forms",
        "id": "762"
      },
      "links": {
        "related": "http://api/api/identities/661/form"
      }
    }
  }
}

An identity object contains the identity data of the user that performed the identification. This object is always included in the response, whatever the status. Values can be null.

Attribute Type Description
first-name string First name of the user
last-name string Last name of the user
birth-date string (format: YYYY-MM-DD) Birth date of the user

Reference data

Identity sent when the identification is created

Attribute Type Description
first-name string First name of the user
last-name string Last name of the user
birth-date string (format: YYYY-MM-DD) Birth date of the user
{
  "type": "reference-data",
  "id": "67",
  "attributes": {
    "last-name": "LASTNAME",
    "first-name": "FIRSTNAME",
    "birth-date": "2000-01-01"
  }
}

Reference data check

The reference-data-check object contains the results of a flexible comparison between reference-data and identity. This check is a security check and allows you to ensure that the person who performed the verification was the one who initiated the process.

Attribute Type Description
score number
  • 1.0: Identity and Reference data match
  • 0.0: Reference data and Identity don't match
  • -1.0: Reference data and Identity could not be matched
{
  "type": "reference-data-checks",
  "id": "61",
  "attributes": {
    "score": 1
  },
  "relationships": {
    "reference-data": {
      "data": {
        "type": "reference-data",
        "id": "67"
      },
      "links": {
        "related": "http://api/api/reference_data_checks/61/reference_data"
      }
    }
  }
}

Documents

document object

{
  "type": "documents",
  "id": "632",
  "attributes": {
    "all-first-names": ["JEAN", "JEANNE"],
    "birth-date": "1975-11-25",
    "birth-place": "PARIS 1ER",
    "car-driving-license-expiry-date": "2023-08-03",
    "document-number": "180975S01202",
    "document-type-detailed": "NULL",
    "document-type": "ID",
    "expiry-date": "2023-08-03",
    "first-name": "JEAN",
    "gender": "M",
    "issue-date": "2013-08-03",
    "issuing-state-code": "FRA",
    "last-name": "VALJEAN",
    "married-name": "NULL",
    "mrz": "IDFRAVALJEAN<<<<<<<<<<<<<<<<<<75500180975S01202JEAN<<<<<<<<<7511251M6",
    "nationality": "FRA",
    "personal-number": "NULL",
    "obtaining-date": "2018-08-03",
    "remarks": "NULL",
    "residence-address": "62 rue de Picpus 75012 Paris",
    "signed-image-back-url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/70f01f19-6ec5-4b14-9b30-2c493e49df15/a92f1974-6d64-4ca7-8266-3c2a573ff55c/tight_crops/FRA-I3-Back-a92f1974-6d64-4ca7-8266-3c2a573ff55c-1551198631997.png?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
    "image-back-hash": "4971510fe454b467d7c931e38c0349f2",
    "signed-image-front-url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/70f01f19-6ec5-4b14-9b30-2c493e49df15/a92f1974-6d64-4ca7-8266-3c2a573ff55c/tight_crops/FRA-I3-Front-a92f1974-6d64-4ca7-8266-3c2a573ff55c-1551198602506.png?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
    "image-front-hash": "4971510fe454b467d7c931e38c0349f2",
    "signed-image-signature-url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/70f01f19-6ec5-4b14-9b30-2c493e49df15/a92f1974-6d64-4ca7-8266-3c2a573ff55c/tight_crops/FRA-I3-Front-a92f1974-6d64-4ca7-8266-3c2a573ff55c-1551198602506.png?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
    "signature-image-hash": "4971510fe454b467d7c931e38c0349f2",
    "signed-video-back-url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/70f01f19-6ec5-4b14-9b30-2c493e49df15/a92f1974-6d64-4ca7-8266-3c2a573ff55c/video/rec-a92f1974-6d64-4ca7-8266-3c2a573ff55c-video-document_0_side_back_document_scene.webm?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
    "video-back-hash": "4971510fe454b467d7c931e38c0349f2",
    "signed-video-front-url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/70f01f19-6ec5-4b14-9b30-2c493e49df15/a92f1974-6d64-4ca7-8266-3c2a573ff55c/video/rec-a92f1974-6d64-4ca7-8266-3c2a573ff55c-video-document_0_side_front_document_scene.webm?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX"
    "video-front-hash": "4971510fe454b467d7c931e38c0349f2",
  },
  "relationships": {
    "identity": {
      "data": {
        "type": "identities",
        "id": "661"
      },
      "links": {
        "related": "http://api/api/documents/632/identity"
      }
    }
  }
}

The document object contains information about the document presented during the identification. This object is included in the response when the identification status is processed. Values can be null.

Attribute Type Description
all-first-names list(string) List of first names extracted from the document (Visual Zone)
birth-date string (format: YYYY-MM-DD) Birth date extracted from the document (Visual Zone)
birth-place string City of birth of the user
car-driving-license-expiry-date string (format: YYYY-MM-DD) Expiration date extracted from the French driving license (type B)
document-number string Document number extracted from the document
document-type string Document category, one of (ID, Passport, Residence Permit, Driving licence, Social Security, Fiscal card, VTC, Visa, Other)
document-type-detailed string Exact type of the document (depends on the document)
expiry-date string (format: YYYY-MM-DD) Expiration date extracted from the document
first-name string First name extracted from the document (Visual Zone)
gender string Gender of the user, M,F or NULL
issue-date string (format: YYYY-MM-DD) Issuing date extracted from the document
issuing-state-code string Document issuing state (3 letters code ISO ALPHA-3)
last-name string Last name extracted from the document (Visual Zone) string
married-name string Married name of the user
mrz string MRZ extracted from the document
nationality string Nationality of the user (3 letters code ISO ALPHA-3)
obtaining-date string (format: YYYY-MM-DD) Obtaining date extracted from the document
personal-number string Personnal number of the user (depends on the exact document)
remarks string Remarks about the document
residence-address string Residence address extracted from the document
signed-image-back-url string Signed URL to access the document back image
signed-image-front-url string Signed URL to access the document front image
signed-image-signature-url string Signed URL to access the document signature image
signed-video-back-url string Signed URL to access the document back video
signed-video-front-url string Signed URL to access the document front video
tax-identification-number string Tax Identification number extracted from the document

Document-checks

document-check object

{
  "type": "document-checks",
  "id": "546",
  "attributes": {
    "expiry-date-score": 1.0,
    "data-extracted-score": 1.0,
    "mrz-validity-score": 1.0,
    "mrz-viz-score": 1.0,
    "ove-score": 1.0,
    "quality-score": 1.0,
    "score": 1.0,
    "supported": 1.0,
    "visual-back-score": 1.0,
    "visual-front-score": 1.0,
    "issue-date-score": 1.0,
    "live-video-capture-score": 1.0
  },
  "relationships": {
    "document": {
      "data": {
        "type": "documents",
        "id": "632"
      },
      "links": {
        "related": "http://api/api/document_checks/546/document"
      }
    }
  }
}

The document-check object contains the checks related to the document itself. This object is included in the response when the identification status is processed.

Attribute Type Description
score number Overall trust score related to the document. It is a synthesis of all the other document-check scores.
  • 1.0: Document is validated
  • 0.0: Document has issues
  • -1.0: Document cannot be processed
quality-score number Indicates if the document video(s) is of sufficient quality.
  • 1.0: Quality is sufficient
  • -1.0: Quality is not sufficient
supported number Indicates if the document presented is in the list of accepted documents.
  • 1.0: Document is accepted
  • 0.0: Document is not accepted
  • -1.0: Document is not presented
data-extracted-score number Indicates if all the data required in the document has been extracted
  • 1.0: All fields are extracted
  • -1.0: At least one field could not be read
expiry-date-score number Indicates if the document presented is expired.
  • 1.0: Document is valid
  • 0.0: Document is expired
  • -1.0: Could not read expiry date
  • null: Document has no expiry date
    issue-date-score number Result of algorithmic checks on the issue date score.
    • 1.0: Issue date is valid
    • 0.0: Issue date does not match requirements
    • -1.0: Could not read issue date
    • null: Document has no issue date
    mrz-validity-score number Result of algorithmic checks on the MRZ.
    • 1.0: All elements are valid
    • 0.0: Some elements are not valid
    • -1.0: Could not read MRZ
    • null: Document has no MRZ
    mrz-viz-score number Result of algorithmic comparison between Visual Zone and MRZ fields.
    • 1.0: All elements match
    • 0.0: Some elements do not match
    • -1.0: Could not read MRZ
    • null: Document has no MRZ
    ove-score number Optically Variable Elements score, for documents which have such elements.
    • 1.0: Document is probably authentic
    • 0.0: Document is probably a fake or a photocopy
    • -1.0: Could not take a decision (e.g. quality is not sufficient)
    • null: document has no OVE to be checked
    visual-front-score number Matching score between the document presented (front side) and the corresponding template.
    • 1.0: Document matches template
    • 0.0: Document does not match template
    • -1.0: Document cannot be processed
    visual-back-score number Matching score between the document presented (back side) and the corresponding template.
    • 1.0: Document matches template
    • 0.0: Document does not match template
    • -1.0: Document cannot be processed
    • null: Document has no back side
    live-video-capture-score number Indicates if fraudulent activity has been detected during the video scene.
    • 1.0: No fraudulent activity detected
    • 0.0: Fraudulent activity detected
    • null: No video has been recorded

    Faces

    face object

    {
      "type": "faces",
      "id": "629",
      "attributes": {
        "actions": "rotation",
        "signed-image-url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/70f01f19-6ec5-4b14-9b30-2c493e49df15/a92f1974-6d64-4ca7-8266-3c2a573ff55c/live_face/a92f1974-6d64-4ca7-8266-3c2a573ff55c-1551198649996.png?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
        "image-hash": "4971510fe454b467d7c931e38c0349f2",
        "signed-video-url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/70f01f19-6ec5-4b14-9b30-2c493e49df15/a92f1974-6d64-4ca7-8266-3c2a573ff55c/video/rec-a92f1974-6d64-4ca7-8266-3c2a573ff55c-video-face.webm?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX"
        "video-hash": "4971510fe454b467d7c931e38c0349f2",
      },
      "relationships": {
        "identity": {
          "data": {
            "type": "identities",
            "id": "661"
          },
          "links": {
            "related": "http://api/api/documents/629/identity"
          }
        }
      }
    }
    

    The face object contains information about the face presented during the identification face scene. This object is included in the response when the identification status is processed.

    Attribute Type Description
    actions string Specify if an action has been asked to the user, and if yes, which action
    signed-image-url string Signed URL to access the face image
    signed-video-url string Signed URL to access the face video

    Face-checks

    face-check object

    {
      "type": "face-checks",
      "id": "546",
      "attributes": {
        "active-liveness-score": 1.0,
        "quality-score": 1.0,
        "score": 1.0,
        "live-video-capture-score": 1.0
      },
      "relationships": {
        "face": {
          "data": {
            "type": "faces",
            "id": "629"
          },
          "links": {
            "related": "http://api/api/face_checks/546/face"
          }
        }
      }
    }
    

    The face-check object contains the checks related to the face itself. This object is included in the response when the identification status is processed.

    Attribute Type Description
    score number Overall trust score related to the face. It is a synthesis of all the other face-check scores.
    • 1.0: Face is validated
    • 0.0: Face has issues
    • -1.0: Face cannot be processed
    quality-score number Indicates if the face video is of sufficient quality.
    • 1.0: Quality is sufficient
    • -1.0: Quality is not sufficient
    active-liveness-score number Indicates if the face presented during the face scene is a real face and not for instance a photo of a face.
    • 1.0: Face is a person
    • 0.0: Face is probably not a person
    • -1.0: Quality of video is not good enough to decide
    live-video-capture-score number Indicates if fraudulent activity has been detected during the video scene.
    • 1.0: No fraudulent activity detected
    • 0.0: Fraudulent activity detected
    • null: No video has been recorded

    Doc-face-matches

    doc-face-match object

    {
      "type": "doc-face-matches",
      "id": "546",
      "attributes": {
        "score": 1.0
      },
      "relationships": {
        "document": {
          "data": {
            "type": "documents",
            "id": "632"
          },
          "links": {
            "related": "http://api/api/doc_face_matches/546/document"
          }
        },
        "face": {
          "data": {
            "type": "faces",
            "id": "629"
          },
          "links": {
            "related": "http://api/api/doc_face_matches/546/face"
          }
        }
      }
    }
    

    The doc-face-match object contains the comparison check between a document and a face. This object is included in the response when the identification status is processed.

    Attribute Type Description
    score number Matching score between the face and the face on the document.
    • 1.0: Document and face are matching
    • 0.0: Document and face are not matching
    • -1.0: Matching cannot be processed

    Doc-doc-matches

    doc-doc-match object

    {
      "type": "doc-doc-matches",
      "id": "62",
      "attributes": {
        "score": 1.0
      },
      "relationships": {
        "document-a": {
          "data": {
            "type": "documents",
            "id": "632"
          },
          "links": {
            "related": "http://api/api/doc_doc_matches/540/document_A"
          }
        },
        "document-b": {
          "data": {
            "type": "documents",
            "id": "639"
          },
          "links": {
            "related": "http://api/api/doc_doc_matches/540/document_B"
          }
        }
      }
    }
    

    The doc-doc-match object contains the comparison check between a two documents, if two documents are presented. This object is included in the response when the identification status is processed.

    Attribute Type Description
    score number Matching score between the information of the first and of the second document.
    • 1.0: Document's data are matching
    • 0.0: They are not matching
    • -1.0: Matching cannot be processed

    Forms

    form object

    {
      "type": "forms",
      "id": "62",
      "attributes": {
        "birth-place-country": "FRA",
        "birth-date": "1978-11-25",
        "birth-place-city": "Paris",
        "birth-place-city-code": "75109",
        "gender": "M",
        "first-name": "JEAN",
        "last-name": "VALJEAN",
        "married-name": "TENARDIER",
        "nationality": "FRA",
        "phone-number": "0102030405",
        "mail-address": "example@domain.com",
        "address-first-line": "5 Rue du Faubourg Saint-Honoré",
        "address-second-line": "FIVE",
        "address-postcode": "75008",
        "address-city": "Paris",
        "address-country": "France",
        "siren": "123456789"
      },
      "relationships": {
        "identity": {
          "data": {
            "type": "identities",
            "id": "661"
          },
          "links": {
            "related": "http://api/api/identities/62/"
          }
        }
      }
    }
    

    The form object contains the information declared by the user about his/her identity. The user is presented a form in the end of the flow, where he/she can amend or enter his/her information. This object is included in the response when the identification status is processing or processed.

    Attribute Type Description
    gender string Gender of the user, M or F
    last-name string Last name of the user
    married-name string Married name of the user
    first-name string First name of the user
    middle-name string Middle name of the user
    birth-date string (format: YYYY-MM-DD) Birth date of the user
    nationality string Nationality of the user, encoded on 3 letters following ISO ALPHA-3
    birth-place-country string Birth country of the user, encoded in 3 letters following ISO ALPHA-3
    birth-place-city string City of birth of the user
    birth-place-city-code string If in France, the city's INSEE code is returned. If not NULL is returned.
    address-first-line string Address line 1 (track number and wording)
    address-second-line string Address line 2 (address supplement)
    address-postcode string ZIP code
    address-city string City of residence
    address-country string Country of residence
    mail-address string Mail address
    phone-number string Phone number
    siren number Company number

    Identity-form-matches

    form object

    {
      "type": "identity-form-matches",
      "id": "425",
      "attributes": {
        "score": 1.0,
        "match": 0.95
      },
      "relationships": {
        "identity": {
          "data": {
            "type": "identities",
            "id": "661"
          },
          "links": {
            "related": "http://api/api/documents/425/identity"
          }
        },
        "form": {
          "data": {
            "type": "forms",
            "id": "861"
          },
          "links": {
            "related": "http://api/api/documents/425/form"
          }
        }
      }
    }
    

    The identity-form-match object contains the results of a strict comparaison between the extracted identity and the form. This check is a compliance check and allows you to ensure that the data declared by the user is correct. This object is included in the response when the identification status is processing or processed.

    Attribute Type Description
    score number Matching score between the identity and the form
    • 1.0: Data are matching
    • 0.0: Data are not matching
    • -1.0: Matching cannot be processed
    match number Percentage of characters matching between the identity and the form

    Endpoints

    Create identification - POST /identifications/

    Example of request:

    curl -X POST \
      https://api.ubble.ai/identifications/ \
      -u {CLIENT_ID}:{CLIENT_SECRET} \
      -H 'Content-Type: application/vnd.api+json' \
      -H 'Accept: application/vnd.api+json' \
      -d '{
      "data": {
        "type": "identifications",
        "attributes": {
          "identification-form": {
            "external-user-id" : "8887-YHYY-98976",
            "phone-number" : "+33666666666",
          },
          "reference-data": {
            "birth-date": "1978-11-25",
            "first-name": "JEAN",
            "last-name": "VALJEAN"
          },
          "webhook": "https://foo.com?titi=toto",
          "redirect_url": "https://bar.com?titi=toto",
          "face_required": true
        }
      }
    }' | jq
    
    import json
    import requests
    
    data = {
      "data": {
        "type": "identifications",
        "attributes": {
          "identification-form": {
            "external-user-id" : "8887-YHYY-98976",
            "phone-number" : "+33666666666",
          },
          "reference-data": {
            "birth-date": "1978-11-25",
            "first-name": "JEAN",
            "last-name": "VALJEAN"
          },
          "webhook": "https://foo.com?titi=toto",
          "redirect_url": "https://bar.com?titi=toto",
          "face_required": true
        },
      }
    }
    
    headers = {
      "Accept": "application/vnd.api+json",
      "Content-Type": "application/vnd.api+json",
    }
    res = requests.post(
      "https://api.ubble.ai/identifications/",
      auth=("CLIENT_ID", "CLIENT_SECRET"),
      data=json.dumps(data),
      headers=headers,
    )
    print(res.json())
    
    const axios = require("axios");
    const config = {
      auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
      headers: {
        Accept: "application/vnd.api+json",
        "Content-Type": "application/vnd.api+json",
      },
    };
    const data = {
      data: {
        type: "identifications",
        attributes: {
          "identification-form": {
            "external-user-id": "8887-YHYY-98976",
            "phone-number": "+33666666666",
          },
          "reference-data": {
            "birth-date": "1978-11-25",
            "first-name": "JEAN",
            "last-name": "VALJEAN",
          },
          webhook: "https://foo.com?titi=toto",
          redirect_url: "https://bar.com?titi=toto",
          face_required: true,
        },
      },
    };
    axios
      .post("https://api.ubble.ai/identifications/", data, config)
      .then((res) => console.log(res.data))
      .catch((err) => console.log(err.response.data));
    

    Response:

    HTTP/1.1 201 Created
    Content-Type: application/vnd.api+json
    

    Content-Type: application/vnd.api+json Accept: application/vnd.api+json

    Used to create an identification. The call will return an identification object.

    Attribute Type Description
    identification-form object Information about the user.
    reference-data object Reference information about the user.
    pre-selected-place-of-residence object Automatically populates the place of residence field in the web application.
    redirect_url string The url where the user will be redirected at the end of the identification. If not set, we'll used the default one created with your Ubble account
    webhook string The url where you will be notified when the identification status changes. If not set, we'll used the default one created with your Ubble account
    face_required boolean Specifies whether the flow should capture the user face or not

    The identification-form field is an object described in the following table.

    Attribute Type Description
    external-user-id string External ID of the user.
    phone-number string (international format) Phone number of the user (optional)

    The reference-data field is an object described in the following table.

    Attribute Type Description
    last-name string Declared last name sent when creating the identification.
    first-name string Declared first name sent when creating the identification
    birth-date string (format: YYYY-MM-DD) Declared birth date sent when creating the identification (optional)

    The pre-selected-place-of-residence field is an object described in the following table.

    Attribute Type Description
    country string (ISO 3166-1 alpha-3) Country of residence.
    subdivision string (ISO 3166-2) Subdivision, e.g US-NY for New-York state. (optional)

    Retrieve an identification - GET /identifications/:identification_id/

    Example of request:

    curl https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/ \
      -u CLIENT_ID:CLIENT_SECRET \
      -H 'Accept: application/vnd.api+json' | jq
    
    import requests
    
    headers = {"Accept": "application/vnd.api+json"}
    res = requests.get(
        "https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/",
        auth=("CLIENT_ID", "CLIENT_SECRET"),
        headers=headers,
    )
    print(res.json())
    
    
    const axios = require("axios");
    const config = {
      auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
      headers: {
        Accept: "application/vnd.api+json",
      },
    };
    axios
      .get(
        "https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/",
        config
      )
      .then((res) => console.log(res.data))
      .catch((err) => console.log(err.response.data));
    

    Response:

    HTTP/1.1 200 OK
    Content-Type: application/vnd.api+json
    

    Returns an identification object and includes related objects depending on identification status :

    Create an image session - POST /identifications/:identification_id/upload/

    Example of request:

    import requests
    
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    identification_id = "identification_id"
    url = f"https://api.ubble.ai/identifications/{identification_id}/upload/"
    payload={
      "doc_country": "doc_country",
      "doc_type": "doc_type"
    }
    
    files = {
        "document-0-image-front": (
            "document-0-image-front.jpeg",
            open(
                "/path/to/image.jpeg",
                "rb",
            ),
            "image/jpeg",
        )
    }
    res = requests.post(url, headers=headers, data=payload, files=files, auth=("CLIENT_ID", "CLIENT_SECRET"))
    print(res.json())
    
    curl --location --request POST 'https://api.ubble.ai/identifications/$IDENTIFICATION_ID/upload/' \
    --form 'document-0-image-front=@/path/to/file.png' \
    --form 'document-0-image-back=@/path/to/file.png' \
    --form 'codec="h264"' \
    --form 'doc_country="doc_country"' \
    --form 'doc_type="doc_type"'
    -u CLIENT_ID:CLIENT_SECRET
    
    const axios = require("axios");
    const FormData = require("form-data");
    const fs = require("fs");
    const data = new FormData();
    const identificationId = "identification_id";
    
    data.append("document-0-image-front", fs.createReadStream("/path/to/file"));
    data.append("document-0-image-back", fs.createReadStream("/path/to/file"));
    data.append("codec", "h264");
    data.append("doc_country", "doc_country");
    data.append("doc_type", "doc_type");
    
    const config = {
      method: "post",
      url: `https://api.ubble.ai/identifications/${identificationId}/upload/`,
      headers: {
        ...data.getHeaders()
      },
      auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
      data : data
    };
    
    axios(config)
    .then(function (response) {
      console.log(JSON.stringify(response.data));
    })
    .catch(function (error) {
      console.log(error);
    });
    
    
    Attribute Type Description
    document-0-image-front file The front picture of the document to verify: jpg, png or pdf. If the file is a PDF, we'll only consider the first page of the pdf.
    document-0-image-back file The back picture of the document to verify: jpg, png or pdf. If the file is a PDF, we'll only consider the first page of the pdf.
    doc_country string The country (in ISO 3 format) of issue of the document to verify. Should be given with a doc_type. Make sure that your Configuration is configured to handle this type of document. This attribute is not mandatory.
    doc_type string The type of the document to verify (see list below). Should be given with a doc_country. Make sure that your Configuration is configured to handle this country. This attribute is not mandatory.

    Response:

    HTTP/1.1 201 OK
    Content-Type: application/json
    {
      "identification_id": "IDENTIFICATION_ID",
      "status": "processed"
    }
    

    Remarks :

    Face Authentication

    The Face Authentication is a feature allowing you to authenticate your users over time, using a shared Identity object.

    Create a Face Authentication

    An Authentication is a special type of Identification, that needs an Identity object in order to perform the authentication.

    You can create an Authentication by providing an identification-type: authentication attribute, as well as an Identity in the create identification endpoint.

    For an Identity to be valid, it needs to have a linked identification whose score is 1 and has usable face assets (not anonymised).

    {
      "data": {
        "type": "identifications",
        "attributes": {
          "identification-type": "authentication"
        },
        "relationships": {
          "identity": {
            "data": {
              "type": "identities",
              "id": "12345"
            }
          }
        }
      }
    }
    

    Errors

    Along with the usual errors described in Errors, this endpoint can yield the following errors :

    Error Description
    400 Bad request
    404 The provided Identity does not exist or does not belong to your configuration
    410 The Identity is valid, but no data allows us to perform the authentication. This perhaps means that the biometric data was anonymized

    If the creation was a success, you will receive an Identification object.

    Once the authentication is processed, you will receive a response that contains, along with the usual object, a face-face-match score that corresponds to the matching between the user's face, and the comparison face.

    {
      "type": "face-face-match",
      "attributes": {
        "score": 0 | 1 | -1
      }
    }
    

    Results

    Once the authentication is processed, you will receive a response that contains the objects identification, face, face-checks, that corresponds to the liveness checks, and a specific face-face-match object that corresponds to the matching between the user's face and the comparison face.

    Anonymize identification - POST /identifications/:identificationId/anonymize/

    Example of request:

    curl -X POST https://api.ubble.ai/identifications/:identificationId/anonymize/ -u {CLIENT_ID}:{CLIENT_SECRET}
    
    import requests
    
    res = requests.post(
      "https://api.ubble.ai/identifications/:identificationId/anonymize/",
      auth=("CLIENT_ID", "CLIENT_SECRET"),
    )
    print(res.status_code)
    
    const axios = require("axios");
    const config = {
      auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
    };
    axios
      .post(
        "https://api.ubble.ai/identifications/:identificationId/anonymize/",
        null,
        config
      )
      .then((res) => console.log(res.status))
      .catch((err) => console.log(err.response.data));
    

    Response:

    HTTP/1.1 202 Accepted
    

    Used to anonymize an identification. Deletes all personal data of the associated identification. Metadata and scores are kept. If you anonymize an identification that is not processed, the identification becomes expired and is not usable anymore.

    Force webhook notification - POST /identifications/:identification_id/notify/

    Example of request:

    curl -X POST \
      https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/notify/ \
      -u {CLIENT_ID}:{CLIENT_SECRET} \
      -H 'Content-Type: application/json' \
      -d '{
      "webhook": "https://webhook.site/"
    }'
    
    import requests
    
    data = {"webhook": "https://webhook.site/"}
    
    res = requests.post(
        "https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/notify/",
        auth=("CLIENT_ID", "CLIENT_SECRET"),
        data=data,
    )
    print(res.status_code)
    
    const axios = require("axios");
    const config = {
      auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
      headers: {
        "Content-Type": "application/json",
      },
    };
    const data = {
      webhook: "https://webhook.site/",
    };
    axios
      .post(
        "https://api.ubble.ai/identifications/11111111-1111-1111-1111-111111111111/notify/",
        data,
        config
      )
      .then((res) => console.log(res.status))
      .catch((err) => console.log(err.response.data));
    

    Response

    HTTP/1.1 204 OK
    
    Attribute Type Description
    webhook string The url to notify. If not given, will used the default webhook set with your account manager

    Returns a 204 no content.

    Endpoints (Test only)

    Retrieve assets - GET /identifications/:identification_id/assets/

    Example of GET request :

    curl https://api.ubble.ai/identifications/:identification_id/assets/ \
      -u CLIENT_ID:CLIENT_SECRET | jq
    
    import requests
    
    res = requests.get(
        "https://api.ubble.ai/identifications/:identification_id/assets/",
        auth=("CLIENT_ID", "CLIENT_SECRET"),
    )
    print(res.json())
    
    
    const axios = require("axios");
    axios
      .get("https://api.ubble.ai/identifications/:identification_id/assets/", {
        auth: { username: "CLIENT_ID", password: "CLIENT_SECRET" },
      })
      .then((res) => console.log(res.data))
      .catch((err) => console.log(err.response.data));
    

    Exammple of response :

    HTTP/1.1 200 OK
    Content-Type: application/json
    
    {
      "face_crops": [
        {
          "url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/live_face/c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335128328.png?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
          "name": "UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/live_face/c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335128328.png"
        }
      ],
      "tight_crops": [
        {
          "url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/tight_crops/FRA-RB6-Front-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335078791.png?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
          "area": 10000,
          "name": "UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/tight_crops/FRA-RB6-Front-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335078791.png",
          "score": 150,
          "template": {
            "id": 1234,
            "code": "RB6",
            "side": "Front",
            "country": "FRA"
          }
        },
        {
          "url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/tight_crops/FRA-RB6-Back-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335122590.png?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX",
          "area": 10000,
          "name": "UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/tight_crops/FRA-RB6-Back-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335122590.png",
          "score": 150,
          "template": {
            "id": 1234,
            "code": "RB6",
            "side": "Back",
            "country": "FRA"
          }
        }
      ],
      "videos": {
        "face": {
          "filename": "rec-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335126735-video-face.mp4",
          "creation_date": 1555335148724,
          "url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/video/rec-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335126735-video-face.mp4?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX"
        },
        "back_id": {
          "filename": "rec-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335093662-video-back_id.mp4",
          "creation_date": 1555335126221,
          "url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/video/rec-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335093662-video-back_id.mp4?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX"
        },
        "front_id": {
          "filename": "rec-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335066884-video-front_id.mp4",
          "creation_date": 1555335095427,
          "url": "https://storage.ubble.ai/production-ubble-ai/UBBLE_DEMO/9d263004-9c2d-4949-93d8-897fcd24129a/c1a6b85e-5dd3-4866-aca0-68678a501d93/video/rec-c1a6b85e-5dd3-4866-aca0-68678a501d93-1555335066884-video-front_id.mp4?response-content-type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXX&X-Amz-Date=XXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX"
        }
      }
    }
    

    It lets you see the different assets generated by Ubble during the session :

    Errors

    Our API returns standard HTTP success or error status codes. For errors, we will also include extra information about what went wrong encoded in the response as JSON. The various HTTP status codes we might return are listed below.

    HTTP Status codes

    Error Code Title Description
    200 OK The request was successful.
    201 Created The resource was successfully created.
    202 Async created The resource was asynchronously created
    400 Bad Request Your request is invalid.
    401 Unauthorized Your API key is wrong.
    403 Forbidden The kitten requested is hidden for administrators only.
    404 Not Found The specified kitten could not be found.
    405 Method Not Allowed You tried to access a kitten with an invalid method.
    406 Not Acceptable You requested a format that is not json-api's one.
    410 Gone The kitten requested has been removed from our servers.
    415 Unsupported Media Type You used a format that is not json-api's one.
    429 Too Many Requests You're requesting too many kittens! Slow down!
    50X Internal Server Error We had a problem with our server. Try again later.
    503 Service Unavailable We're temporarily offline for maintenance. Please try again later.

    Error types

    Example error response.

    {
      "errors": [
        {
          "detail": "Authentication credentials were not provided.",
          "source": {
            "pointer": "/data"
          },
          "status": "401"
        }
      ]
    }
    

    All errors are returned in the form of JSON with a type and optional message.

    Type Description
    params_invalid Your parameters were not valid.
    unknown_record Record was not found.
    unknown_route URL was not valid.
    queued Lookup queued. Try this request again in a few minutes.
    rate_limit The request has been rate limited.
    api_error Internal API error.

    Service Status

    To access the service current status, one can use the Ubble status page or its API. Documentation about the API is available here : https://status.ubble.ai/api

    In the Ubble status page api response, you can find the status. The status gives you a description and an indicator (a level of outage).

    Service in description Indicator Meaning
    API major The service is unavailable
    API partial The service is unavailable
    IDApp major The service is unavailable
    IDApp partial The verification service is slower, users are not impacted, but the response delay might be longer
    Dashboard all The outage has no operational impact on the user nor on the verification service

    Versioning

    When we make backwards-incompatible changes to the API, we release new, dated versions. The current version is v0.4. Read our changelog to learn more about backwards compatibility. Note that events generated by API requests will always be structured according to your account API version.

    Changelog

    Version Date
    v1.23 2022-12-07
    v1.22 2022-08-25
    v1.21 2022-07-05
    v1.20 2022-06-13
    v1.19 2022-05-10
    v1.18 2022-02-10
    v1.17 2021-11-26
    v1.16 2021-05-05
    v1.15 2021-03-18
    v1.14 2021-02-26
    v1.13 2021-02-25
    v1.12 2021-02-22
    v1.11 2021-01-19
    v1.10 2020-12-04
    v1.9 2020-11-10
    v1.8 2020-10-07
    v1.7 2020-08-31
    v1.6 2020-03-02
    v1.5 2020-02-19
    v1.4 2019-09-04
    v1.3 2019-02-26
    v1.2 2019-01-16
    v1.1 2018-09-01

    v1.23 2022-12-07

    v1.22 2022-08-25

    v1.21 2022-07-05

    v1.20 2022-06-13

    v1.19 2022-05-10

    v1.18 2022-02-10

    v1.17 2021-11-26

    v1.16 2021-05-05

    v1.15 2021-03-18

    v1.14 2021-02-26

    v1.13 2021-02-25

    v1.12 2021-02-22

    v1.11 2021-01-19

    v1.10 2020-12-04

    v1.9 2020-11-10

    v1.8 2020-10-07

    v1.7 2020-08-31

    v1.6 2020-03-02

    v1.5 2020-02-19

    v1.4 2019-09-04

    v1.3 2019-02-26

    v1.2 2019-01-16

    v1.1 2018-09-01