Deepsight is Incode's liveness technology. During capture, the device collects extra signals: depth data, a short selfie video, and motion data. The back end uses these signals to find spoofing attempts.
This page shows how to use Deepsight with Capture-Only Mode. In Capture-Only Mode, the Android SDK captures the images and signals but does not send them to Incode; your integration sends them with the Omni API. Use this mode when your app needs to control when and how captures are sent to your own back end. This page gives the SDK configuration, the order of the API calls, and a sample request for each call, for two flows:
- Basic flow: An ID Capture module and a Selfie module. The user scans an ID and takes a selfie with Deepsight.
- Video Selfie Flow: The basic flow plus a Video Selfie module. The user records a video that can hold a second ID scan, a selfie, and voice consent. The back end compares this recording with earlier captures.
You can also run these flows without Deepsight.
Before You Start
You need these four items:
- An SDK integrated in
SdkMode.CAPTURE_ONLY. See Capture-Only Mode. - Android SDK 5.54.0 or later. Earlier versions do not have the Deepsight configuration API. Versions 5.50.0 to 5.53.x have it, but
SelfieScanResult.presignedVideoFilePathcan reference a file that does not exist, which stopsomni/recordings/import. - An API key for your environment.
- A Dashboard flow (
configurationId) with:- Deepsight turned on in the Flow or Workflow Settings tab.
- The Deepsight settings of the Face Capture module set to match the SDK
modality. - Face Capture and ID Capture modules with Motion set to match the SDK
motion.
Warning
Set the Deepsight and Motion options in the SDK to the same values as the flow configuration in Dashboard. The two control different things: Dashboard tells the back end which signals to expect, and the SDK configuration decides which signals the device collects. If they differ, the back end rejects the call with Invalid capture metadata. The next section gives the two settings.
The examples use WELCOME_API_URL and WELCOME_API_KEY, the same names as the installation examples. Replace them with the API URL and the API key of your environment.
Warning
Make these calls from your back end. The API key gives full access to your environment. Do not put the API key in the app, and do not call the Omni API from the device. The app sends the captures to your back end, and your back end makes the calls on this page.
Every call to WELCOME_API_URL after omni/start needs these headers. An upload to a pre-signed URL does not use them.
| Header | Value |
|---|---|
x-api-key |
WELCOME_API_KEY, the API key of your environment. |
api-version |
1.0 |
X-Incode-Hardware-Id |
The token value that omni/start returned. |
Content-Type |
application/json, with two exceptions. Step 15 of the Video Selfie flow sends the audio file with audio/mp4. The part upload of the alternative upload route sends each chunk with application/octet-stream. |
SDK Configuration for Deepsight
Deepsight needs two changes in the SDK code, one for the ID scan and one for the selfie scan.
ID Scan
IdScan.Builder.setMotionEnabled(Boolean) is available since Android SDK 5.50.0. It writes the motion flag into the capture metadata. The default value is false.
Set the same value as the Motion option of the ID Capture module in Dashboard. If the two differ, the back end rejects omni/add/front-id/v2 with Invalid capture metadata.
val idScan = IdScan.Builder()
.setIdType(IdScan.IdType.ID)
.setMotionEnabled(true)
.build()
IdScan idScan = new IdScan.Builder()
.setIdType(IdScan.IdType.ID)
.setMotionEnabled(true)
.build();
Selfie Scan
SelfieScan.Builder.setDeepsightConfiguration(DeepsightConfiguration) is available since Android SDK 5.50.0. It replaces setVideoLivenessRecordingEnabled(Boolean), which is deprecated. See the Migration Guide.
The configuration has three independent fields:
| Field | Default | Description |
|---|---|---|
setEnabled(Boolean) |
true |
Turns Deepsight liveness on or off. When false, the SDK ignores the modality. |
setModality(Modality) |
SINGLE_FRAME |
The capture mode. The SDK applies it only when enabled is true. |
setMotionEnabled(Boolean) |
false |
Turns motion collection on or off. This field is independent of the other two. |
The three modality values are:
| Modality | Depth data | Video recording |
|---|---|---|
SINGLE_FRAME |
No | No |
MULTIMODAL |
Yes | No |
VIDEO_LIVENESS |
Yes | Yes |
val selfieScan = SelfieScan.Builder()
.setDeepsightConfiguration(
DeepsightConfiguration.Builder()
.setEnabled(true)
.setModality(DeepsightConfiguration.Modality.VIDEO_LIVENESS)
.setMotionEnabled(true)
.build()
)
.build()
SelfieScan selfieScan = new SelfieScan.Builder()
.setDeepsightConfiguration(
new DeepsightConfiguration.Builder()
.setEnabled(true)
.setModality(DeepsightConfiguration.Modality.VIDEO_LIVENESS)
.setMotionEnabled(true)
.build()
)
.build();
Info
The SDK applies setDeepsightConfiguration() and IdScan.Builder.setMotionEnabled() in SdkMode.CAPTURE_ONLY only. In other modes, the server configuration has priority and the SDK writes a warning to the log.
Result Fields
Read these fields from the capture results and send them to the API:
| Callback | Result | Field | Send it to |
|---|---|---|---|
onIdFrontCompleted() |
IdScanResult |
idImageBase64, metadata |
omni/add/front-id/v2 |
onIdBackCompleted() |
IdScanResult |
idImageBase64, metadata |
omni/add/back-id/v2 |
onSelfieScanCompleted() |
SelfieScanResult |
fullFrameSelfieBase64, metadata |
omni/add/face/third-party |
onSelfieScanCompleted() |
SelfieScanResult |
presignedVideoFilePath |
omni/recordings/import |
onVideoRecorded() |
VideoSelfieResult |
videoFilePath |
The URL from omni/generateVideoSelfieUrl |
onVideoRecorded() |
VideoSelfieResult |
idFrontImagePath |
omni/videoselfie/compare-id/v2, omni/videoselfie/compare-ocr/v2 |
onVideoRecorded() |
VideoSelfieResult |
idBackImagePath |
omni/videoselfie/compare-back-id/v2, omni/videoselfie/compare-back-ocr/v2 |
Info
The SDK populates metadata in SdkMode.CAPTURE_ONLY only. In every other mode the field is null.
The SDK populates videoFilePath and presignedVideoFilePath on SelfieScanResult in SdkMode.CAPTURE_ONLY, and only when the modality is VIDEO_LIVENESS. In every other case both fields are null. Send presignedVideoFilePath, and keep SelfieScanResult.videoFilePath for local diagnosis only.
Every asset field of VideoSelfieResult is a file path. The result has no base64 field. Read each file, then encode the image files as base64 for the JSON requests. Send the audio file and the video recording as binary.
Basic Flow
Use this flow for Deepsight with an ID scan and a selfie scan.
Call the endpoints in this order. A tag shows which capture module a step belongs to. Make a tagged step only when that module is in the flow. A step with no tag always applies.
POST /omni/startPOST /omni/add/device-fingerprintID scan, front side:
POST /omni/add/front-id/v2?onlyFront=falseID scan, back side:
POST /omni/add/back-id/v2ID scan:
POST /omni/process/idSelfie with Deepsight video:
POST /omni/recordings/importSelfie Scan:
POST /omni/add/face/third-party?imageType=selfie&recordingId=RECORDING_IDSelfie Scan:
POST /omni/process/face?imageType=selfiePOST /omni/finish-status
Warning
The onlyFront query parameter of step 3 tells the back end whether a back image follows.
- To scan both sides, use
IdScan.Builder.setScanStep(ScanStep.BOTH), sendonlyFront=false, and make step 4. - To scan the front only, use
setScanStep(ScanStep.FRONT), sendonlyFront=true, and omit step 4.
ScanStep.BOTH is the default.
Info
Send the device fingerprint with omni/add/device-fingerprint directly after omni/start, before any capture. The SDK does the same in SdkMode.STANDARD: it sends the device fingerprint as part of the session setup, before the first module starts. Send it once for each session.
Which Steps Apply
Each tagged step needs its module in the flow and its data in the result.
| Steps | Make them when | Flow configuration | Result field |
|---|---|---|---|
| 3 | the flow scans the front of a document | FlowConfig.Builder.addID(...) |
IdScanResult.idImageBase64 from onIdFrontCompleted() |
| 4 | the flow scans the back of a document | addID(...) with IdScan.Builder.setScanStep(ScanStep.BOTH), the default |
IdScanResult.idImageBase64 from onIdBackCompleted() |
| 5 | the flow scans a document | addID(...) |
none, this call carries no image |
| 6 | the selfie records a video for deepsight | setDeepsightConfiguration(...) with Modality.VIDEO_LIVENESS |
SelfieScanResult.presignedVideoFilePath |
| 7, 8 | the flow takes a selfie | FlowConfig.Builder.addSelfieScan(...) |
SelfieScanResult.fullFrameSelfieBase64 |
If a module is not in the flow, its callback never fires and its result field is never set. If a module is in the flow and its field is null, the capture did not complete. Record the fault, as described for the Video Selfie Flow.
Start the Session
omni/start creates the interview. The response gives the token value for the X-Incode-Hardware-Id header of every later call.
curl --location 'WELCOME_API_URL/omni/start' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--data '{
"configurationId": "YOUR_FLOW_ID"
}'
Add the Device Fingerprint
Deepsight needs the device fingerprint. Read it from the SDK with IncodeWelcome.getInstance().getDeviceFingerprint().toJson(). This method returns the exact JSON body for the call. Send it without changes.
val fingerprintJson = IncodeWelcome.getInstance().getDeviceFingerprint().toJson()
String fingerprintJson = IncodeWelcome.getInstance().getDeviceFingerprint().toJson();
curl --location 'WELCOME_API_URL/omni/add/device-fingerprint' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data "$FINGERPRINT_JSON"
The body has this shape. The values change with the device and the SDK version, so do not copy them.
{
"sdkVersion": "5.50.0",
"hash": "1234",
"ip": "10.11.11.11",
"osVersion": "31",
"deviceModel": "MAO-LX9",
"data": "{\"crossApp\":true,\"totalRam\":8018219008,\"availableRam\":4677599232}",
"deviceType": "ANDROID",
"algorithmName": "androidId",
"algorithmVersion": "1.0.0"
}
Add the Front of the ID
Send idImageBase64 and metadata from the IdScanResult that onIdFrontCompleted returns.
curl --location 'WELCOME_API_URL/omni/add/front-id/v2?onlyFront=false' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "result.idImageBase64",
"metadata": "result.metadata"
}'
Add the Back of the ID
Send idImageBase64 and metadata from the IdScanResult that onIdBackCompleted returns.
curl --location 'WELCOME_API_URL/omni/add/back-id/v2' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "result.idImageBase64",
"metadata": "result.metadata"
}'
Process the ID
curl --location 'WELCOME_API_URL/omni/process/id' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{}'
Send the Deepsight Video
SelfieScanResult.presignedVideoFilePath is a file path, not the video itself. Read that file as plain text. Then send the text in the video field without a change. The contents are already base64. Do not encode them again.
If presignedVideoFilePath is null, there is no recording to send. Omit this step, and send the selfie without a recordingId.
With the VIDEO_LIVENESS modality this field must hold a path. A null value means the recording failed, and the session gets no video liveness check. Record the fault.
curl --location 'WELCOME_API_URL/omni/recordings/import' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"type": "selfie",
"video": "CONTENTS_OF_PRESIGNED_VIDEO_FILE"
}'
The response gives a recordingId. Keep this value for the next call.
Send the Selfie Scan Image
Send fullFrameSelfieBase64 and metadata from the SelfieScanResult that onSelfieScanCompleted returns. Add the recordingId from the previous step as a query parameter.
curl --location 'WELCOME_API_URL/omni/add/face/third-party?imageType=selfie&recordingId=RECORDING_ID' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "result.fullFrameSelfieBase64",
"metadata": "result.metadata"
}'
Process the Selfie Scan Image
This call runs the face checks on the selfie from step 7. The request body is empty.
curl --location 'WELCOME_API_URL/omni/process/face?imageType=selfie' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{}'
Finish the Session
This call closes the session. Make it after all the capture and process calls. The request body is empty.
curl --location 'WELCOME_API_URL/omni/finish-status' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{}'
Video Selfie Flow
Use this flow for Deepsight with an ID scan, a selfie scan, and a Video Selfie.
Call the endpoints in this order. A tag shows which capture module a step belongs to. Make a tagged step only when that module is in the flow. A step with no tag always applies.
POST /omni/startPOST /omni/add/device-fingerprint- ID scan, front side:
POST /omni/add/front-id/v2?onlyFront=false - ID scan, back side:
POST /omni/add/back-id/v2 - ID scan:
POST /omni/process/id - Selfie Scan with Deepsight video:
POST /omni/recordings/import - Selfie Scan:
POST /omni/add/face/third-party?imageType=selfie&recordingId=RECORDING_ID - Selfie Scan:
POST /omni/process/face?imageType=selfie - Video Selfie, front document:
POST /omni/videoselfie/compare-id/v2 - Video Selfie, front document:
POST /omni/videoselfie/compare-ocr/v2 - Video Selfie, back document:
POST /omni/videoselfie/compare-back-id/v2 - Video Selfie, back document:
POST /omni/videoselfie/compare-back-ocr/v2 - Video Selfie, image:
POST /omni/add/face/third-party?imageType=videoSelfie - Video Selfie, image:
POST /omni/process/face?imageType=videoSelfie - Video Selfie, speech check:
POST /omni/add/speech?audioOnly=true - Video Selfie, consent face check:
POST /omni/videoselfie/voiceconsent/add/face GET /omni/generateVideoSelfieUrlPUTthe Video Selfie recording to the returned URLPOST /omni/finish-status
The questions and the consent phrase come from a call you make before the module starts. It is not part of this sequence. See Get the Questions and the Consent Phrase.
Warning
Voice consent for the record needs no API call. Steps 15 and 16 ask the back end to check the consent. They are not what records it. The Video Selfie recording already holds the audio and the video of the user who speaks the phrase, and steps 17 and 18 send that recording.
If you collect the consent as a record only, omit steps 15 and 16. Omit them even when setVoiceConsentEnabled(true) is set. Make step 15 to check the words the user said. Make step 16 to match the face of the user during the consent.
Steps 1 to 8 and step 19 are the same as the Basic Flow.
Which Steps Apply
Each tagged step needs its Video Selfie step to have run and put its data in the result. Steps 17 and 18 carry no tag because the Video Selfie always records.
| Steps | Make them when | Builder option | Default | Result field |
|---|---|---|---|---|
| 9, 10 | the Video Selfie scans the front of the document | setIdScanEnabled(true) |
on | idFrontImagePath |
| 11, 12 | the Video Selfie scans the back of the document | setIdScanEnabled(true) |
on | idBackImagePath |
| 13, 14 | the Video Selfie takes a selfie | setSelfieScanEnabled(true) |
on | selfieImagePath |
| 15 | the recording has audio and you want the back end to check what the user said | setVoiceConsentEnabled(true) or setRandomQuestionsEnabled(true) |
off | audioFilePath |
| 16 | the consent has a face frame and you want the back end to match that face | setVoiceConsentFaceRecognitionEnabled(true), setVoiceConsentEnabled(true), and setSelfieScanEnabled(true). All three are necessary. |
off | voiceConsentSelfiePath |
setDisableAudio(true) stops the recording of audio. The default is false. With audio off, audioFilePath and voiceConsentSelfiePath are null and you omit steps 15 and 16.
The four comparison options (setFrontIdComparisonEnabled, setFrontIdOcrComparisonEnabled, setBackIdComparisonEnabled, and setBackIdOcrComparisonEnabled) have no effect in SdkMode.CAPTURE_ONLY. The SDK makes no comparison calls in this mode. Your back end decides which comparisons to make when it makes or omits steps 9 to 12.
Info
A null field is not always a skipped step. Your configuration decides which calls to make. The result field tells you what to send.
If you did not enable a step, its field is null and you omit its calls. That is the normal case.
If you did enable a step and its field is null, the capture did not complete. Do not treat this as a skipped step. Record the fault and examine it. The session continues without that check, and no error tells you so.
Get the Questions and the Consent Phrase
Make this call only if you want the back end to check the words the user said.
Make it before the Video Selfie module starts. It returns the question and answer pairs of your API key. Give that text to the SDK with setRandomQuestions(...) and the voice consent options, so the user reads the same text that the back end scores in step 15. If the text on screen is not the same text, the check fails for a user who said the phrase correctly.
In SdkMode.CAPTURE_ONLY the SDK does not make this call. It shows only the text you give it. In SdkMode.STANDARD the SDK makes the call itself at the start of the step and shows what it returns.
curl --location 'WELCOME_API_URL/omni/get/questionAndAnswer?numberOfQuestions=1&returnVoiceConsentQuestion=true' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN'
The response gives a list of questions. The entry with id 0 is the consent phrase.
Steps 9 to 12: Compare the Documents
The comparison calls use the ID frames that the Video Selfie module captured, not the frames from steps 3 and 4. Read the file at VideoSelfieResult.idFrontImagePath or VideoSelfieResult.idBackImagePath. Then encode the bytes as base64 with no line breaks.
| Step | Endpoint | Image |
|---|---|---|
| 9 | omni/videoselfie/compare-id/v2 |
idFrontImagePath |
| 10 | omni/videoselfie/compare-ocr/v2 |
idFrontImagePath |
| 11 | omni/videoselfie/compare-back-id/v2 |
idBackImagePath |
| 12 | omni/videoselfie/compare-back-ocr/v2 |
idBackImagePath |
Send each pair of calls only when the matching frame is in the result:
- If
idFrontImagePathis set, make steps 9 and 10. - If
idBackImagePathis set, make steps 11 and 12. - If a field is
null, omit the two steps for that side. If the Video Selfie scanned that side, anullvalue means the capture failed.
This is the same rule the SDK applies in the STANDARD mode. There, the SDK gives the Video Selfie one scan step for each side of the document, then compares each side as it captures it.
VideoSelfie.Builder.setIdScanEnabled(true) gives the Video Selfie a front step and a back step, so a two-sided document sets both fields and all four steps apply. In SdkMode.CAPTURE_ONLY the comparison options do not remove any of these steps. To omit a comparison, do not make its call.
Info
The result gives paths, not images. VideoSelfieResult has no base64 fields. Your app must read each file and encode it.
Step 9: Compare the ID
This call makes sure that the document in the Video Selfie is the same type as the document from step 3.
curl --location 'WELCOME_API_URL/omni/videoselfie/compare-id/v2' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "BASE64_OF_ID_FRONT_IMAGE_PATH"
}'
Response (200):
{
"typeOfId": "DriversLicense",
"idTypeMatched": true,
"issueYear": 2017,
"faceRecognitionConfidence": 1.0,
"idSideMatched": true
}
Response (400):
{
"timestamp": 1775238442064,
"status": 4020,
"error": "Image to compare not found.",
"message": "BadRequestException: Unable to compare images - template not found",
"path": "/omni/videoselfie/compare-id/v2"
}
| Error code | Meaning |
|---|---|
1003 |
Face cropping failure. |
4019 |
Face not found. |
4020 |
Image to compare not found. |
5003 |
Unsatisfied image size. |
5004 |
Document alignment failed. |
5005 |
Document not found. |
Step 10: Compare the OCR Data
This call makes sure that the data on the document in the Video Selfie is the same as the data from step 3.
curl --location 'WELCOME_API_URL/omni/videoselfie/compare-ocr/v2' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "BASE64_OF_ID_FRONT_IMAGE_PATH"
}'
Response (200):
{
"success": true
}
A 400 response has the same shape as step 9. The error codes are 5003, 5004, and 5005.
Steps 11 and 12: Compare the Back of the Document
These two calls are the back-side counterparts of steps 9 and 10. Send the base64 of the file at VideoSelfieResult.idBackImagePath.
curl --location 'WELCOME_API_URL/omni/videoselfie/compare-back-id/v2' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "BASE64_OF_ID_BACK_IMAGE_PATH"
}'
curl --location 'WELCOME_API_URL/omni/videoselfie/compare-back-ocr/v2' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "BASE64_OF_ID_BACK_IMAGE_PATH"
}'
The responses have the same shape as steps 9 and 10.
Steps 13 and 14: Send the Video Selfie Image
These two calls are optional. Make them when the Video Selfie takes a selfie, so when selfieImagePath is set.
Steps 13 and 14 use the same two endpoints as steps 7 and 8, but they send a different image. Step 7 sends the image from the Selfie Scan module. Step 13 sends the image that the Video Selfie module captured during the video. The imageType query parameter tells the back end which image it receives.
| Steps | Module | Image | imageType |
|---|---|---|---|
| 7, 8 | Selfie Scan | SelfieScanResult.fullFrameSelfieBase64 |
selfie |
| 13, 14 | Video Selfie | VideoSelfieResult.selfieImagePath |
videoSelfie |
Warning
FACE_MATCH changes the imageType. The value of imageType in steps 13 and 14 follows VideoSelfie.Builder.setSelfieMode(...):
SelfieMode.SELFIE_MATCH, the default, sendsimageType=videoSelfie. The back end compares the Video Selfie image with the earlier selfie.SelfieMode.FACE_MATCHsendsimageType=selfie. The Video Selfie image becomes the selfie of the session, and the back end compares it with the ID.
The examples below show the default.
curl --location 'WELCOME_API_URL/omni/add/face/third-party?imageType=videoSelfie' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "BASE64_OF_SELFIE_IMAGE_PATH"
}'
Step 14 processes that frame. The response gives a confidence value. Keep it for step 16.
curl --location 'WELCOME_API_URL/omni/process/face?imageType=videoSelfie' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN'
Response (200):
{
"confidence": 1
}
Step 15: Send the Voice Consent Audio
This call is optional. Make it to check the words the user said. It needs audioFilePath to be set. Omit it if you collect the consent as a record only. The recording in steps 17 and 18 holds the audio either way.
Before the module starts, get the questions and the consent phrase and give them to the SDK. See Get the Questions and the Consent Phrase.
Step 15 sends the recording at VideoSelfieResult.audioFilePath. The body is the audio file itself, not JSON, so this call uses Content-Type: audio/mp4. The speech recognition runs inside this call. There is no process step after it.
curl --location 'WELCOME_API_URL/omni/add/speech?audioOnly=true' \
--header 'Content-Type: audio/mp4' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data-binary '@/path/to/audioFilePath.mp4'
Warning
The iOS SDK uses a different speech endpoint. The iOS SDK calls omni/add/speech/v2 with a JSON body. The Android SDK calls omni/add/speech?audioOnly=true with the audio file as the request body. If you write one back end for both platforms, make it accept the shape each platform sends.
Step 16: Send the Voice Consent Face
Optional. Make this call to match the face of the user during the consent. It needs voiceConsentSelfiePath to be set.
Omit it if you do not want this second face check. The consent is still recorded.
Send the frame at VideoSelfieResult.voiceConsentSelfiePath as base64, with the confidence value from step 14. The threshold is the bar the score is measured against. The SDK sends the recognition threshold, which you set with CommonConfig.Builder.setRecognitionThreshold(...). The default is 0.6.
curl --location 'WELCOME_API_URL/omni/videoselfie/voiceconsent/add/face' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"base64Image": "BASE64_OF_VOICE_CONSENT_SELFIE_PATH",
"confidence": 1,
"threshold": 0.6
}'
Step 17: Get the Upload URL
This call reserves a pre-signed Amazon S3 URL for the Video Selfie recording. Make it after the comparison and face calls, then send the recording to that URL in step 18.
curl --location 'WELCOME_API_URL/omni/generateVideoSelfieUrl' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN'
The response gives the URL for the recording:
{
"url": "PRESIGNED_UPLOAD_URL"
}
Step 18: Send the Recording
Send the file at VideoSelfieResult.videoFilePath to the url from step 17.
curl --location --request PUT 'PRESIGNED_UPLOAD_URL' \
--header 'Content-Type: video/mp4' \
--data-binary '@/path/to/videoSelfie.mp4'
To send the recording in parts instead of one request, read Alternative: Upload the Video Selfie in Parts.
Alternative: Upload the Video Selfie in Parts
Both Video Selfie flows send the recording to a pre-signed S3 URL in one request. Three Omni endpoints send the same recording in parts instead.
Use it in place of steps 17 and 18 of the Video Selfie Flow. Use one route or the other, never both.
Select this route when one of these is true:
- Your security policy does not permit a direct upload to Amazon S3.
- The recording is large, and one request for the whole file is not reliable.
Info
The SDK does not use this route. In the STANDARD mode the SDK always reserves one URL and sends the recording in a single request. The endpoints below are an API-only route for Capture-Only mode.
Start the Upload
The response gives an uploadId. Every later call needs this value.
curl --location --request POST 'WELCOME_API_URL/omni/video-selfie/upload/initiate' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN'
Send the Parts
The API gateway rejects a request larger than 10 MB. Divide the video into parts of about 7 MB, which keeps each request below that limit. For example, split -b 7000000 archive.mp4 chunk_ writes the files chunk_aa, chunk_ab, and chunk_ac. Do not send parts near the 10 MB limit. Each request carries more than the part itself, so parts of 9 MB can still fail. 7 MB is the tested value.
Send each part in its own call. Increase partNumber by 1 for each part.
curl --location 'WELCOME_API_URL/omni/video-selfie/upload/part?uploadId=UPLOAD_ID&partNumber=1' \
--header 'Content-Type: application/octet-stream' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data-binary '@/path/to/chunk_aa'
Each response gives an eTag. Keep each eTag with its part number.
Complete the Upload
This call tells the back end to put the parts together into one recording. Send the uploadId from the first call and the partNumber and eTag of each part.
curl --location 'WELCOME_API_URL/omni/video-selfie/upload/complete' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"uploadId": "abc123",
"parts": [
{ "partNumber": 1, "eTag": "bcae19" },
{ "partNumber": 2, "eTag": "d4f921" },
{ "partNumber": 3, "eTag": "a1b2c3" }
]
}'
Obey these four rules for the upload in parts:
- Read the
uploadIdfrom the response of the first call. Do not write it into the code. - Number the parts in sequence, from 1.
- Send the parts in the same order as the divided files.
- Put every part in the
partsarray. A missing part makes the assembly fail.
Flows Without Deepsight
Every flow on this page uses Deepsight. To run the same flow without it, make these two changes:
- In the SDK, set
setEnabled(false)on theDeepsightConfiguration, and setsetMotionEnabled(false)on both theDeepsightConfigurationand theIdScan.Builder. Set the same values in the Dashboard flow. - Omit the Deepsight video. With
setEnabled(false)the SDK records no video, sopresignedVideoFilePathisnull. OmitPOST /omni/recordings/importand omit therecordingIdquery parameter ofomni/add/face/third-party.
Every other call stays, in the same order.
Additional APIs
These calls are optional. They read data from a session that is complete.
Get the Session Score
curl --location 'WELCOME_API_URL/omni/get/score' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN'
Get the OCR Data of the ID
curl --location 'WELCOME_API_URL/omni/get/ocr-data' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN'
Get the Session Images
Base64 strings are large. Ask for the images that you need, in one call.
curl --location --request POST 'WELCOME_API_URL/omni/get/images' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN' \
--data '{
"images": [
"fullFrameFrontID",
"fullFrameBackID",
"selfie",
"signatureImageID",
"ghostSignatureImageID",
"ghostPortraitImageID"
]
}'
Get the Deepsight Video
curl --location 'WELCOME_API_URL/omni/generateSessionRecordingDownloadUrl?interviewId=INTERVIEW_ID&type=selfie' \
--header 'Accept: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: WELCOME_API_KEY' \
--header 'X-Incode-Hardware-Id: SESSION_TOKEN'
Clean Up the Local Data
Call IncodeWelcome.deleteUserLocalData(context) after the flow exits. Call it from the success, error, and cancellation callbacks. See Capture-Only Mode.
Send or copy every capture you need before you make this call. On the error and cancellation paths the uploads can be incomplete, and the call removes the files permanently.
For additional help, see the Android API Reference or contact Incode support.