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).

  1. From Applications , click Add application .
  2. Add the friendly application name to display in the Admin Portal.
  3. 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.

  4. 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:

Copy
Copied
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.

Copy
Copied
 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:

Copy
Copied
 {
    "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 or selfie
Copy
Copied
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 as true .
  • 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:

Copy
Copied
{
  "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:

Copy
Copied
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:

Copy
Copied
{
  "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:

Copy
Copied
{
  "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" ]
    }
  }
}