Verify documents with backend-to-backend integration
You can use identity verification to securely verify the identity of your customers using documents like their driver’s license or passport—such as before allowing them to open a new bank account online or pick up a rental car. This describes how to add identity verification to your application using a backend-only integration, and your frontend experience (if relevant).
How it works
Here's an example of a basic integration flow. Transmit APIs are shown in pink along with the relevant integration step.
When the user performs an action requiring identity verification, a session is created from the backend to establish a secure context for the flow (Step 3). Your app collects the required images and adds them to the session (Step 4). Once all required images are added, your app starts polling for the verification result (Step 6), and then proceeds accordingly.
Step 1: Create your app
To integrate with Transmit, you'll need to create an application in the Admin Portal (if you don’t have one yet).
- From Applications , click Add application .
- Add the friendly application name to display in the Admin Portal.
-
Add a client display name, and your website URL as a redirect URI (e.g.,
https://your-domain.com
).Note
These fields are required for Transmit apps, but won’t be used for the identity verification flow.
- Click Add to create your application. This will automatically generate your client credentials.
Step 2: Get access token
You’ll need an OAuth2 bearer access token to authorize the backend API requests. Using the client ID and client secret of your Transmit application, send this request from your backend to generate an access token:
curl -i -X POST \
https://api.transmitsecurity.io/oidc/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_secret=[CLIENT_SECRET]' \
-d grant_type=client_credentials \
-d resource=https://verify.identity.security \
-d 'client_id=[CLIENT_ID]'
Note
The token must be requested for the https://verify.identity.security
resource, which will appear in the audience claim of the generated token (in the future we’ll block access to tokens without this audience). The token must remain secure on your server, and must only be used for backend requests.
Step 3: Create session
A session is required to provide a secure context for the identity verification flow. Using the access token generated in Step 2, create a session by sending a backend request like the one below. Make sure to set auto_start
to true
, which is required for verifications performed via backend-only integrations.
curl --location --request POST 'https://api.transmitsecurity.io/verify/api/v1/verification
' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer [ACCESS_TOKEN]' \
--data-raw '{
"auto_start": true
}'
The response contains a session_id
that will be used to identify the session for subsequent calls. For example:
{
"session_id": "ahJ3xZwZ2O1s",
"expiration": "1867-11-07T00:00:00.000Z"
}
Step 4: Add images
The verification process requires images of the user's government-issued ID (both the front and back) and a selfie. Once a session is started, initiate the user verification flow used to capture these images (for example, using your verification UI).
Note
Image requirements: min width 640px, max width 3840px, min height 480px, max height 2160px.
Once an image is captured, save it as JPEG and add it to the session by sending a backend request like below, where:
-
[ACCESS_TOKEN]
is the access token retrieved in step 2 -
[SESSION_ID]
is the session ID returned in step 3 -
[CONTENT]
is the captured image encoded as a base64 string -
[IMAGE_TYPE]
is the type of image:document_front
,document_back
orselfie
curl -i -X POST \
'https://api.transmitsecurity.io/verify/api/v1/verification/[SESSION_ID]/images' \
-H 'Authorization: Bearer [ACCESS_TOKEN]' \
-H 'Content-Type: application/json' \
-d '{
"image": {
"format": "jpg",
"image_type": "[IMAGE_TYPE]",
"content": "[CONTENT]"
}
}'
Step 5: Handle image feedback
If the request to add an image is successful (201), the response includes feedback that indicates whether or not the image was accepted (based on image quality, document type, etc.):
-
if
ok
is returned, the image was accepted and you can proceed with adding the next image (as described in step 5). Once all required images are added,complete
will be returned astrue
. - otherwise, the image wasn't accepted and the feedback will indicate why. The user should be guided to retake the image based on the feedback provided:
Feedback | Description |
---|---|
document_not_found |
The image doesn't appear to contain a document (when it should based on the image type) |
document_not_supported |
The image contains a document that isn't supported (see Supported documents) |
obstructed |
Certain details are not visible in the image |
blurry |
The image is blurry |
glare |
There's a glare in the image |
document_not_matching |
The document front and back don't match |
other |
There's some other issue with the image |
Here's an example response for a document image that was successfully submitted:
{
"feedback": "ok",
"complete": "false",
"missing_images": [
"selfie"
]
}
Step 6: Get verification result
Once all the required images are successfully submitted, Transmit automatically starts processing the verification and your backend should start polling the /result endpoint for the verification result:
curl -i -X GET \
'https://api.transmitsecurity.io/verify/api/v1/verification/[SESSION_ID]/result' \
-H 'Authorization: Bearer [ACCESS_TOKEN]'
While the verification is still being processed, the status
response field is returned as processing
and polling should continue. Once the verification result is ready, the status
is complete
and the response contains the result.
Step 6: Handle verification result
Your service should define the user experience based on the verification result, indicated by the recommendation
field:
-
If
ALLOW
: the identity verification process was completed successfully. The response includes user details collected from the document (like their name and birthday), which can be used to enrich the user's profile, and details about the document used to prove their identity. -
If
CHALLENGE
: the identity verification process didn’t succeed, since at least one verification check didn’t pass. The response includes extracted (yet unverified) info and indicates which checks didn't pass and why. This info can be used to review unsuccessful sessions or analyze failed verification attempts. You should proceed as suitable for your use case, typically by initiating a manual review process. -
If
DENY
: the identity verification indicates a high likelihood of attempted fraud. The response includes the extracted (yet unverified) info and indicates which checks didn't pass and why. You should block the user or initiate an in-depth manual review to avoid onboarding a fraudulent user.
Collected information is arranged inside nested objects: person
, document
, and additional_info
.
Besides the composite verification result, the response provides information about individual checks inside the nested checks
object.
Here's a response example for successful ID verification:
{
"session_id": "H1I12oskjzsdhskj4",
"status": "complete",
"recommendation": "ALLOW",
"person": {
"full_name": "Marie Salomea Skłodowska-Curies",
"given_name": "Marie",
"surname": "Curies",
"national_id": "123ABC",
"date_of_birth": "1867-11-07T00:00:00.000Z"
},
"document": {
"country": "US",
"region": "NY",
"type": "national_id",
"number": "1234567",
"serial_number": "1234567",
"issue_date": "1867-11-07T00:00:00.000Z",
"expiration_date": "1867-11-07T00:00:00.000Z"
},
"additional_info": {
"address": {
"country": "USA",
"region": "Indiana",
"city": "Indianapolis",
"street": "Snowy Ridge Road",
"house_number": "1234",
"apartment_number": "12",
"postcode": "56789",
"full_address": "1234 Snowy Ridge Road Indianapolis, IN 56789"
},
"national_status": {
"citizen": true,
"resident": true
},
"employment": {
"profession": ""
}
},
"checks": {
"document_validation": {
"recommendation": "ALLOW"
},
"document_authentication": {
"recommendation": "ALLOW"
},
"document_liveness": {
"recommendation": "ALLOW"
},
"biometric_matching": {
"recommendation": "ALLOW"
},
"biometric_liveness": {
"recommendation": "ALLOW"
},
"flagged_identity": {
"recommendation": "ALLOW"
},
"risk_recommendation": {
"recommendation": "ALLOW"
}
}
}
The response indicates when checks fail and why:
{
"session_id": "string",
"status": "complete",
"recommendation": "DENY",
// EXTRACTED DATA...
"checks": {
"document_validation": {
"recommendation": "ALLOW"
},
"document_authentication": {
"recommendation": "ALLOW"
},
"document_liveness": {
"recommendation": "DENY",
"reasons": [ "presentation_attack" ]
},
"biometric_matching": {
"recommendation": "CHALLENGE",
"reasons": [ "biometric_mismatch" ]
},
"biometric_liveness": {
"recommendation": "DENY",
"reasons": [ "mask detected" ]
},
"flagged_identity": {
"recommendation": "DENY",
"reasons": [ "multiple_same_identity_attempts" ]
},
"risk_recommendation": {
"recommendation": "DENY",
"reasons": [ "action_is_suspected_fraud" ]
}
}
}