Get Started
Welcome to Ubble's Certified product API documentation !
In the dashboard, in the developper space, you will be able to generate you mutual TLS
credentials. 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.
Mutual TLS Authentication
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 in the Dashboard. 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 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.
Create an identification
Create an identification
curl -X POST 'https://api.ubble.ai/identifications/' \
-u CLIENT_ID:CLIENT_SECRET \
--header 'Content-Type: application/vnd.api+json' \
--cert /path/client.crt \
--data-raw '{
"data": {
"type": "identifications",
"attributes": {
"reference-data": {
"first_name":"JOHN",
"last_name":"DOE"
}
}
}
}'
--key /path/client.key | jq
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",
},
body: {
"data": {
"type": "identifications",
"attributes": {
"reference-data": {
"first_name":"JOHN",
"last_name":"DOE"
}
}
}
}
};
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) => {
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"),
cert=('/path/client.crt', '/path/client.key'),
data={
"data": {
"type": "identifications",
"attributes": {
"reference-data": {
"first_name":"JOHN",
"last_name":"DOE"
}
}
}
},
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:
- Create identification object in your backend for a user
- Redirect user to the identification-url in your frontend
- Manage user return
- Get automatic answer
- Get final answer
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), as well as the declared first-name
and last-name
(in the reference-data
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.
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 :
- iOS >= 13, on iOS >= 14.3 WebRTC features work perfectly inside the WKWebView.
- android - webview version >= 53
- react-native - react-native webview >= 8.0
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
width
: Iframe width in pxheight
: Iframe height in pxallowCamera
: Boolean, indicating whether you want to capture video inside the iframe. This field should be false if you redirect the users via Desktop-to-Mobile redirectionidentificationUrl
: Identification url (e.g. https://id.ubble.ai/identification-id) events:onComplete(completeEvent)
: Event triggered when the user completes the identification
{
"status": "processing",
"redirectUrl": "https://your-redirect-url.com?identification_id=<ubble identification id>"
}
onAbort(abortEvent)
: Event triggered when the user aborts the identification
{
"status": "aborted",
"returnReason": "NO_DOCUMENT",
"redirectUrl": "https://your-redirect-url.com?identification_id=<ubble identification id>"
}
onExpired
: Event triggered when the user's identification has expired
{
"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
- Make sure you did not forget to add a semicolon in the header
Content-Security-Policy: frame-src https://*.domain_name.fr;
- Make sure beforehand the use of the camera has been allowed
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 | connection_issue | none | The connexion was not good enough. |
aborted | doc_instructions_not_followed | challenge_timeout | The user did not follow the instruction for the document challenge. |
aborted | face_instructions_not_followed | challenge_timeout | The user did not follow the instruction for the face challenge. |
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
. 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 |
external-identity | Additional data sent about the user |
document | Object storing all the data extracted from the user's document |
face | Object storing all the data extracted from the user's face |
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 , external-identity |
processed | Identification is completely processed by Ubble | identification , identity , form , document , face , identity-form-match , reference-data , external-identity |
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 non-categorizable 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 |
2399 | Generic code when a fraud has been detected within a certified identity verification |
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 incomplete.
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 |
---|
The document video resolution is too low |
The document is not presented |
The document video quality is too low |
Back of the document is not presented |
Back of the document video quality is too low |
Some required data of the document could not be extracted |
Face is not presented |
Face video quality is too low |
Authenticity of the document could not be verified |
User liveness could not be verified |
Face and the document photo match could not be verified |
User did not follow the instructions when submitting their face |
User did not follow the instructions when submitting their document |
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
:
- with a default one for all identifications that you create (see with your account manager)
- when you create your identification see create identification endpoint
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 :
- with a default one for all identifications that you create (see with your account manager)
- when you create your identification see create identification endpoint
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.
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 | Text field in case of incomplete identification (score value is -1) |
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.
|
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 |
- When the status is
unitiated
,initiated
oraborted
all values will be NULL. - When the status is
processing
the identity data is our best guess based on an automatic analysis. This information can be useful to perform automatic form filling but is not Ubble’s final answer. It might be adjusted at a later stage. - When the status is in
processed
, the answer is final and will not be modified.
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"
}
}
External identity
Additional data sent about the user
Attribute | Type | Description |
---|---|---|
user-id | string | Id of the external user |
phone-number | string | Phone number of the external user |
mail-address | string | Mail address of the external user |
{
"type": "external-identity",
"id": "67",
"attributes": {
"user-id": "user-1337",
"phone-number": "0102030405",
"mail-address": "mail@exemple.com"
}
}
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",
"video-back-hash": "4971510fe454b467d7c931e38c0349f2",
"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 |
tax-identification-number | string | Tax Identification number extracted from the document |
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",
"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 |
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
|
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",
"mail-address": "mail@exemple.com",
},
"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",
"mail-address": "mail@exemple.com",
},
"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",
"mail-address": "mail@exemple.com",
},
"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) |
mail-address | string | Mail address 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 :
- when identification status is
processing
,uninitiated
oraborted
it includes onlyidentity
- when identification status is
processed
, it includesidentity
,face
,document
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
- Can be used on any identification.
- Triggers a webhook call to simulate a notification from status change.
- Re-triggers a webhook.
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"
}
}
}
- Can be used for test account only. Once live, this endpoint becomes unavailable.
- Fetch the assets of an identification with a completed session. Therefore, identification status needs to be in
processing
,processed
.
It lets you see the different assets generated by Ubble during the session :
tight_crops
: cropped images of the user's documentface_crops
: cropped images of the user's face
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
- Add Certified Documentation at
certified.html
v1.22 2022-08-25
- Update comment
- Add more details to code for POST for image upload
- Add known issues for iframe integration
v1.21 2022-07-05
- Update
residence-address
v1.20 2022-06-13
- Add
residence-address
- Add
car-driving-license-expiry-date
v1.19 2022-05-10
- Add
all-first-names
- Add
tax-identification-number
v1.18 2022-02-10
- Change Iframe SDK address to
cdn.ubble.ai
v1.17 2021-11-26
- Update Desktop-to-Mobile documentation
v1.16 2021-05-05
- Updated iframe sdk url
v1.15 2021-03-18
- Added
comment
field documentation
v1.14 2021-02-26
doc_country
anddoc_type
forPOST /identifications/:identification_id/upload/
endpoint is now verified with your Configuration settings.
v1.13 2021-02-25
- Several updates about integration
- Added Iframe documentation
- Added reference dara & reference data check
- Fix misc. typos
v1.12 2021-02-22
- Add note on 14.3 iOS webview compatibility
v1.11 2021-01-19
- Add Face auth documentation
v1.10 2020-12-04
- Add
doc_country
anddoc_type
parameters toPOST /identifications/:identification_id/upload/
endpoint
v1.9 2020-11-10
- Add
configuration
field in webhook body.
v1.8 2020-10-07
- Added
identity-form
field inPOST identification
v1.7 2020-08-31
- Deprecate
init-no-video
- Add
upload/
endpoint
v1.6 2020-03-02
- Added
siren
form field. - Added
phone-number
form field. - Added
mail-address
form field. - Added
address-first-line
form field. - Added
address-second-line
form field. - Added
address-postcode
form field. - Added
address-city
form field. - Added
address-country
form field. - Added
middle-name
form field.
v1.5 2020-02-19
- Added
init-no-video
endpoint to create a session to validate documents froms pictures
v1.4 2019-09-04
- Lifecycle:
client_notified
status does not exists anymore - Lifecycle: a new status
aborted
informs you of an identification which could not be completed by the user, possibly for technical reasons - Webhook is now notified at each status changed
- Identification
score
is now only available forprocessed
identifications - Added form and identity-form-match objects
- Added parameter
face_required
in the identification creation to handle presence of face - Added fields in Document:
document-type-detailed
,expiry-date
,married-name
,birth-place
,gender
,nationality
,personal-number
,remarks
- Added doc-doc-match object
- Added a link to the video file in Faces
object
:signed-video-url
- Added a link to the video files in Documents
object
:signed-video-front-url
andsigned-video-back-url
v1.3 2019-02-26
- Added objects in the
GET
response
v1.2 2019-01-16
- Added customizable redirect URL and webhook
v1.1 2018-09-01
- First version