Integration¶
Architecture overview¶
To access the XpressID service, Veridas will provide you with the following credentials:
- XpressID_URL: Base URL used by the native SDK when calling
XpressID.start. - XpressID_API_URL: Authentication API base URL used by your backend to obtain access tokens and configure the onboarding flow.
- API_KEY: Unique identifier for your client account. This key must remain exclusively in your backend.
Veridas will provide the appropriate URLs and credentials for your environment and region.
| Url name | Environment | Region | Url value |
|---|---|---|---|
| XpressID_URL | Sandbox AWS | EMEA | https://xpressid-web-work.eu.veri-das.com/ |
| AMERS | https://xpressid-web-work.us.veri-das.com/ | ||
| Sandbox GCP | EMEA | https://xpressid-web-work.eu8.veri-das.com/ | |
| Production AWS | EMEA | https://xpressid-web.eu.veri-das.com/ | |
| AMERS | https://xpressid-web.us.veri-das.com/ | ||
| Production GCP | EMEA | https://xpressid-web.eu8.veri-das.com/ | |
| XpressID_API_URL | Sandbox AWS | EMEA | https://api-work.eu.veri-das.com/xpressid/api/v3/ |
| AMERS | https://api-work.us.veri-das.com/xpressid/api/v3/ | ||
| Sandbox GCP | EMEA | https://api-work.eu8.veri-das.com/xpressid/api/v3/ | |
| Production AWS | EMEA | https://api.eu.veri-das.com/xpressid/api/v3/ | |
| AMERS | https://api.us.veri-das.com/xpressid/api/v3/ | ||
| Production GCP | EMEA | https://api.eu8.veri-das.com/xpressid/api/v3/ |
To ensure compliance with Veridas' security protocols, clients must provide information regarding the server IPs where XpressID integration is planned. This requirement applies only to production environments.
Achieving a functional integration of the XpressID service within your infrastructure requires two coordinated parts.
Authentication API¶
Access tokens must always be requested from your backend. That request is also the place where you send the onboarding configuration JSON for the flow you want to run.
The API_KEY is especially sensitive and must never be embedded in the app. Recommended backend safeguards include:
- user authentication before requesting a token
- abuse prevention such as captcha or rate limiting
- DDoS mitigation
- token issuance controls and auditability
You can review the token request shape in the API documentation here.
Native SDK¶
The iOS SDK is integrated as a native library and launched with two runtime values:
XpressID_URLACCESS_TOKEN
The SDK then handles the onboarding lifecycle on-device and notifies your app when the process finishes or emits intermediate events.
The recommended architecture of a native application using XpressID should follow the next diagram.

| Interaction | Description |
|---|---|
| 1 | The user logs in within your application. |
| 2 | Your backend requests a token from XpressID Auth API using XpressID_API_URL and API_KEY. |
| 3 | XpressID Auth API returns the token response. |
| 4 | Your backend returns the ACCESS_TOKEN to the app. |
| 5 | The app uses XpressID_URL and ACCESS_TOKEN to launch the native SDK. |
| 6 | The SDK notifies the app when the process completes or when intermediate events are emitted. |
Module integration¶
The following steps present the preparations that must be made in order to use the XpressID module within your iOS application.
- Create a new Xcode Swift project, or use an existing one.
- Add the required permissions into
Info.plist:- Privacy - Camera Usage Description
- Privacy - Microphone Usage Description
- Privacy - Location When In Use Usage Description
- iPad only —
UIRequiresFullScreen(set toYES) to ensure the SDK can control the screen orientation.
- If any of your onboarding configurations enables NFC, enable NFC reading in the app, as explained in NFC reading.
- Create a new group named
Frameworksinside the project. - Drag and drop all XpressID distributables (
.xcframework), includingVeridasNfcSDK.xcframework, into theFrameworksgroup and select:- Action: Copy files to destination
- Groups: Create groups
- Targets: Check your target
- Set the embed option for all xcframeworks to
Embed & Signin the target’s General tab. - Make sure the
User Script Sandboxingbuild option is set toNoin the target's Build Settings tab. - Import
VeridasXpressIDand implementXpressIDDelegate. - If you want to receive process events, also implement
XpressIDEventsDelegateand register it withXpressID.setEventsDelegate(delegate:). - Call
XpressID.start()with a validXpressID_URLand access token.
Once the XpressID process completes or an error occurs,
onXpressIDFinished()will be invoked by the SDK.
NFC reading¶
The NFC chip of the document is only read when the onboarding configuration enables it (see NFC step). In that case, the host app must declare the NFC capability and the related Info.plist keys. Without them, the system does not let the app open an NFC reading session.
-
In Signing & Capabilities, add the Near Field Communication Tag Reading capability. The App ID and the provisioning profile must carry the same capability. The app entitlements must include:
<key>com.apple.developer.nfc.readersession.formats</key> <array> <string>TAG</string> </array> -
Add the application identifiers of the eMRTD chip to
Info.plist:<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key> <array> <string>A0000002471001</string> <string>A00000024710FF</string> </array> -
Add the usage description shown by the system when the reading starts to
Info.plist:<key>NFCReaderUsageDescription</key> <string>We use NFC to read the chip of your identity document.</string>
NFC reading needs a physical iPhone. iPads have no NFC reader: on a device that cannot read NFC, the NFC step is skipped and the onboarding continues.
Configuration payload¶
The current native SDK uses the API v3 configuration payload. For new integrations, build the onboarding configuration around:
flowSetupsetuptextsmediasstyles
Those sections can be sent inline in the JSON payload or split into separate texts, medias, and styles files when your backend requests the access token with multipart/form-data.
If you are still migrating from an older version, check the migration guides.
NFC step¶
The NFC reading is a step of a document capture: it runs after the document has been captured, and only for the captures where flowSetup.options.document.captures[].nfc.enabled is true. The data needed to open the chip is obtained from the back end, so the form to enter the document data is normally skipped.
"flowSetup": {
"stages": ["document"],
"options": {
"document": {
"captures": [
{
"documentTypes": ["SelectedByUser"],
"nfc": {
"enabled": true,
"setup": {
"maxRetries": 3
}
}
}
]
}
}
}
| Parameter | Description |
|---|---|
enabled |
Reads the NFC chip of the document captured in this step. |
setup.maxRetries |
Maximum number of retries after a failed reading. |
setup.instructionsShow |
Shows the NFC instructions screen. |
setup.cancelButtonInstructionsShow |
Shows the cancel button of the NFC instructions screen. When it is not set, the global setup value is used. |
setup.uiOrientation |
Orientation of the NFC screens: portrait or landscape. |
The logo and close button visibility follow the global setup. The texts and media of the NFC screens are set per document capture in texts.document[].nfc and medias.document[].nfc (see Customization).
When NFC is enabled and the device can read NFC, the reading is mandatory: if it cannot be done, the onboarding ends with an error (see NFC-related errors).
States and provinces in the document selector¶
The document selector can ask for the state or province of the document after the country, for the countries whose states or provinces are reported by the back end (the United States and Canada). It is disabled by default. To enable it, set flowSetup.options.document.captures[].documentSelector.setup.regionChooser.enabled to true:
"documentSelector": {
"setup": {
"regionChooser": {
"enabled": true
}
}
}
Without this option, the document selector works as before. The texts of the state and province selectors are set in texts.document[].documentSelector (see Customization).
Events¶
The optional onXpressIDEvent(event:) callback receives XpressIDEvent instances on the main thread.
An event has this public shape:
{
"type": "info",
"code": "ProcessEvents",
"message": "Information about events during the process",
"infoEvent": {
"code": "DocumentObverseDetection",
"detail": "The obverse of the document has been captured."
},
"eventListened": "VD_capture_obverse",
"documentIndex": 0
}
The relevant fields are:
type:infoorerrorcode: top-level event family. In practice the SDK currently emits:ProcessEventsProcessCancelledProcessCompleted
message: top-level message associated withcodeinfoEvent.code: detailed event code that identifies the concrete lifecycle transitioninfoEvent.detail: human-readable detail for that concrete eventeventListened: raw internal event name received from the underlying stage SDK, when applicabledocumentIndex: zero-based document capture index for document-stage and NFC eventsselfieNotCenteredTime: continuous time, in seconds, associated withSelfieFaceNotCenterednfcData: data of the NFC reading, only in some NFC events:retriesinNfcReadFailed, and the result of the chip checks inNfcPassiveAuthChecked(accessProtocol,correctlySigned,dataIntegrityVerified,chipAuthentication,activeAuthentication,trustworthy,paceProtocol,initialSelectedAID,readBlockSize)
Fields without a value are not included in the event.
Event families¶
Global and selector events¶
| type | infoEvent.code | infoEvent.detail |
|---|---|---|
| info | SelectorProcessStarted | Started document selector by country. |
| info | ClickOnDocumentSelector | Clicked any document option of the selector. |
| info | SelectorProcessFinished | Finished document selector by country. |
| info | ACSelectorProcessStarted | Started autoclassification selector. |
| info | ClickOnDocumentACSelector | Clicked one of the optioncs of the acselector. |
| info | ACSelectorProcessFinished | Finished autoclassification selector. |
| info | StartUserProcess | XpressID has been initialized. |
| info | EndUserProcess | XpressID has been finished. |
| info | ProcessCancelled | The process has been canceled. |
| info | ProcessCompleted | The process has been successfully completed. |
Document events¶
| type | infoEvent.code | infoEvent.detail |
|---|---|---|
| info | DocumentProcessStarted | Document capture process has been started. |
| info | DocumentInstructions | Document instructions shown. |
| info | DocumentMounted | Document capture process has been prepared. |
| info | DocumentUnmounted | Document capture process has been finished. |
| info | DocumentLoaded | Document capture process has been loaded. |
| info | DocumentCameraPermissionsGranted | Camera permissions have been granted in the document process. |
| info | DocumentCameraStarted | The camera has been started in the document process. |
| info | DocumentCameraVideoPlayStarted | The camera is launched correctly on document process. |
| error | DocumentCameraFailureDefaultError | There was a problem starting the camera in the document process. |
| error | DocumentCameraFailurePermissionError | There was a permission problem starting the camera in the document process. |
| info | DocumentSuccessTickFinish | The success tick is fired correctly on document process. |
| info | DocumentReviewImage | Review image has been displayed in the document process. |
| info | DocumentContinueClicked | The user has clicked the continue review image button in the document process. |
| info | DocumentObverseDetection | The obverse of the document has been captured. |
| info | DocumentReverseDetection | The reverse of the document has been captured. |
| info | DocumentProcessFinished | Document capture process has been finished. |
| info | DocumentCloseButtonClicked | The close button has been clicked in the document process. |
| info | DocumentRepeatClicked | The document capture process has been repeated. |
| error | DocumentDetectionTimeout | Timeout has been exceeded. |
| info | DocumentRestartClicked | The user has clicked the restart button in the document process. |
| info | DocumentRestartProcess | The document capture process needs to restart because an error occurred |
| info | DocumentManualCapture | The document has been captured manually. |
| error | DocumentConfigValidationError | The configuration is invalid in the document process. |
| info | DocumentRestartFromModal | DocumentRestartFromModal |
| info | DocumentLoseFocusError | The user has lost the focus on the application. |
NFC events¶
The NFC events belong to the document capture where the NFC reading runs, identified by documentIndex.
| type | infoEvent.code | infoEvent.detail |
|---|---|---|
| info | NfcProcessStarted | NFC read process has been started. |
| info | NfcFlowStarted | NFC capture flow has been started. |
| info | NfcInstructions | NFC instructions shown. |
| info | NfcMrzEntered | The user submitted document credentials (MRZ fields or CAN) in the NFC process. |
| info | NfcReadStarted | A chip read attempt has started in the NFC process. |
| info | NfcReadSucceeded | The chip was read successfully in the NFC process. |
| error | NfcReadFailed | A chip read attempt failed in the NFC process. |
| error | NfcWrongKey | The entered credentials did not unlock the chip in the NFC process. |
| info | NfcPassiveAuthChecked | Passive Authentication was evaluated for a successful read in the NFC process. |
| info | NfcCancelled | The user closed the NFC flow themselves. |
| info | NfcCloseButtonClicked | Close button has been clicked in the NFC process. |
| info | NfcRestartClicked | The user has clicked the restart button in the NFC process. |
| info | NfcLoseFocusError | The user has lost the focus on the application. |
| info | NfcProcessFinished | NFC read process has been finished. |
Selfie events¶
| type | infoEvent.code | infoEvent.detail |
|---|---|---|
| info | SelfieProcessStarted | Selfie capture process has been started. |
| info | SelfieInstructions | Selfie instructions shown. |
| info | SelfieMounted | Selfie capture process has been prepared. |
| info | SelfieUnmounted | Selfie capture process has been finished. |
| info | SelfieLoaded | Selfie capture process has been loaded. |
| info | SelfieFlowStarted | Selfie flow started |
| info | SelfieFaceNotCentered | The face is not centered in the selfie process. |
| info | SelfieCameraPermissionsGranted | Camera permissions have been granted in the selfie process. |
| info | SelfieCameraStarted | The camera has been started in the selfie process. |
| info | SelfieCameraVideoPlayStarted | The camera is launched correctly on selfie process. |
| info | SelfieCameraRecorderSetup | The camera has started recording in the selfie process. |
| info | SelfieHeadOkMovement | SelfieHeadOkMovement |
| error | SelfieHeadWrongMovementError | SelfieHeadWrongMovementError |
| error | SelfieHeadMovementTimeoutError | SelfieHeadMovementTimeoutError |
| info | SelfieCameraRecorderStop | There was a problem starting the camera in the selfie process. |
| info | SelfieSuccessTickFinish | The success tick is fired correctly on selfie process |
| info | SelfieFaceDetection | Face has been successfully captured in selfie process. |
| info | SelfieCaptured | Selfie has been successfully captured. |
| info | SelfieHeadCaptured | Selfie head has been successfully captured. |
| info | SelfieProcessFinished | Selfie capture process has been finished. |
| error | SelfieCameraFailureDefaultError | There was a problem starting the camera in the selfie process. |
| error | SelfieCameraFailurePermissionError | There was a permission problem starting the camera in the selfie process. |
| error | SelfieDetectionTimeout | Timeout has been exceeded in selfie process. |
| info | SelfieRepeatClicked | The selfie capture process has been repeated. |
| info | SelfieCloseButtonClicked | Close button has been clicked in the selfie process. |
| info | SelfieRestartClicked | The user has clicked the restart button in the selfie process. |
| info | SelfieRestartProcess | The selfie capture process has been restart because an error occurred. |
| info | SelfieRestartFromModal | SelfieRestartFromModal |
| info | SelfieRestartToken | New challenge is generated during the selfie process |
| error | SelfieCameraRecorderSetupError | An error has been found during the camera setup of the selfie process. |
| info | SelfieContinueClicked | The user has clicked the continue button in image review screen during the selfie process. |
| info | SelfieReviewImage | Review image has been displayed in the selfie process. |
| error | SelfieConfigValidationError | The configuration is invalid in the selfie process. |
| info | SelfieLoseFocusError | The user has lost the focus on the application. |
| error | JwtPublicKeyNotFound | The JWT public key was not found in the configuration |
| error | SelfieInvalidChallengeSignature | The challenge signature is invalid |
Video events¶
| type | infoEvent.code | infoEvent.detail |
|---|---|---|
| info | VideoProcessStarted | Video capture process has been started. |
| info | VideoInstructions | Video instructions shown. |
| info | VideoMounted | Video capture process has been prepared. |
| info | VideoUnmounted | Video capture process has been finished. |
| info | VideoLoaded | Video capture process has been loaded. |
| info | VideoCameraPermissionsGranted | Camera permissions have been granted in the video process. |
| info | VideoCameraStarted | The camera has been started in the video process. |
| info | VideoCameraVideoPlayStarted | The camera is launched correctly on video process. |
| info | VideoCameraRecorderSetup | The camera has started recording in the video process. |
| info | VideoFaceDetection | The face has been captured correctly in the video process. |
| info | VideoObverseDetection | The document obverse has been captured in the video process. |
| info | VideoCameraRecorderStop | The camera has been stopped in the video process. |
| info | VideoReverseDetection | The document reverse has been captured in the video process. |
| info | VideoSuccessTickFinish | The success tick is fired correctly on video process. |
| info | VideoCaptureDetection | Video has been successfully captured. |
| info | VideoProcessFinished | Video capture process has been finished. |
| error | VideoCameraFailureDefaultError | There was a problem starting the camera in the video process. |
| error | VideoCameraFailurePermissionError | There was a permission problem starting the camera in the video process. |
| info | VideoCloseButtonClicked | Close button has been clicked in the video process. |
| info | VideoRestartProcess | The video capture process has been restart because an error occurred. |
| info | VideoRestartFromModal | VideoRestartFromModal |
| info | VideoProcessRestarted | The video capture process has been restarted |
| info | VideoRestartClicked | The user has clicked the restart button in the video process. |
| error | VideoDetectionTimeout | Timeout has been exceeded in video process. |
| info | VideoFaceCenterOrientationError | The face is not centered in the video process. |
| error | VideoConfigValidationError | The configuration is invalid in the video process. |
| info | VideoLoseFocusError | The user has lost the focus on the application. |
Important distinction¶
XpressIDEventis the non-terminal event stream emitted during the flow.XpressIDError, returned inonXpressIDFinished(error:), is the terminal error object used when the SDK closes because the flow cannot continue.
XpressIDError¶
When an error occurs, XpressID tries to handle it transparently. However, certain errors cannot be recovered inside the SDK. In those cases the module closes and returns an XpressIDError instance through onXpressIDFinished.
XpressIDError conforms to Codable and can be represented as:
{
"type": "error",
"code": "code",
"message": "message",
"stage": "stage"
}
This JSON is also accessible through the description instance variable as a String.
The fields are:
typeis always"error"in the current public model.codeis the machine-readable error identifier that your app should use for routing, analytics, or user-facing fallback logic.stagedepends on where the error occurred. It can be one of:"document""selfie""video"
messagecan be either:- a curated SDK default message
- a backend-provided message when the backend returned a more specific detail
Public error codes returned by the SDK¶
General and backend-related¶
| code | Default message |
|---|---|
| AvailableTypesError | Available types error. |
| ConfigurationError | A configuration error occurred. |
| TokenNotAllowed | Token has been expired |
| OnboardingCompleted | The onboarding process has already been completed |
| ConnectionError | There has been a problem with the connection |
| ApiTimeoutError | Connection with the api has exceeded the timeout. |
| ValidationError | The provided data is not valid |
| ErrorProcessingRequest | Processing previous request |
| ErrorUpdateValidation | Validation deleted during processing |
| UnknownDocumentTypeError | Document Analysis error: The provided image could not be classified. Please verify that the image and the document type are correct. |
| UnknownAnalysisTypeError | Unknown analysis type |
| InvalidData | Invalid json data |
| ScoresConfigurationNotAllowed | ScoresConfiguration field is just allowed in obverse analysis |
| ScoresConfigurationError | Scores configuration has an incorrect value |
| InvalidScoresConfigurationField | ScoresConfiguration field contains invalid JSON |
| UnallowedLeversError | Document Analysis error: The following levers could not be found please check for errors |
| LogError | An error occurred while sending events to the server. |
| ProcessCompleteError | An error occurred while completing the process. |
| ProcessInformationError | An error occurred while processing the information. |
| StopError | The operation has been stopped by calling stop(). |
| UnknownError | An unknown error occurred: {description}. |
| UserClosedError | User closed XpressID intentionally. |
Document-related¶
| code | Default message |
|---|---|
| DocumentAnalysisError | {side} document analysis error: the provided image has given an error while analyzing. |
| DocumentTypeError | Error retrieving the document type |
| DocumentTypeMismatchError | The provided document does not match the document type sent |
| EmptyDocumentError | Empty document file |
| InvalidImageType | Invalid image type |
| ImageAlreadyAnalyzedError | The provided image type has already been uploaded and analyzed |
| ErrorProcessingFile | The file couldn't be processed properly |
| DocumentServiceNotReachable | Service url not set |
| DocumentNfcUnavailableError | Document Analysis error: NFC is not available for this document type |
| BlurredImageError | Document Analysis error: The provided image does not meet the minimum quality |
| AdditionalDocumentProcessError | Main document not analyzed |
| DocumentAnalyzingError | Document Analysis error: The provided image has given an error while analyzing |
Selfie-related¶
| code | Default message |
|---|---|
| SelfieAnalysisError | Selfie analysis error. |
| SelfieChallengeError | Selfie challenge retrieval error. |
| MaxRetriesExceeded | Maximum number of retries exceeded. |
| JwtPublicKeyNotFound | The JWT public key was not found in the configuration |
| SelfieInvalidChallengeSignature | The challenge signature is invalid |
In a Selfie Alive Pro process, the SDK verifies the signature of the challenge before starting it. If the configuration received from the back end does not include the public key to verify it, the onboarding ends with JwtPublicKeyNotFound as soon as the configuration is loaded. If the signature is not valid, it ends with SelfieInvalidChallengeSignature.
Video-related¶
| code | Default message |
|---|---|
| VideoAnalysisError | An error occurred while analysing the video. |
NFC-related¶
When a document capture has NFC enabled and the device can read NFC, the NFC reading is mandatory: if it cannot be done, the onboarding ends with an error, reported with stage document. A device without NFC skips it.
| code | Default message |
|---|---|
| DocumentNfcError | Error retrieving the document NFC data. |
| NfcUploadError | Error uploading the document NFC data. |
If the user closes the NFC reading, the error is UserClosedError. An invalid NFC configuration is reported as CaptureConfigValidationError. Any other failure of the reading is a CaptureError whose message names it (for example passiveAuthenticationFailed or maxRetriesReached).
Capture-related (document, selfie, video and NFC)¶
These errors are reported when a capture SDK closes with an error. The stage field (document, selfie or video) identifies which capture produced it; the NFC reading is part of the document stage.
| code | Default message |
|---|---|
| CameraPermissionError | Camera permission was not granted. |
| CameraFailureError | There was a problem starting the camera. |
| CaptureTimeoutError | The capture timeout has been exceeded. |
| CameraRecorderSetupError | An error occurred while setting up the camera recorder. |
| CaptureConfigValidationError | The capture configuration is not valid. |
| CaptureError | The capture finished with an error. |
Resume process¶
In the idv operation mode, a user may abandon the process or encounter an error before completion. In those cases, the recommended approach is to request a new token using the resume operation mode and the corresponding validation identifier.
Typical scenarios:
- The process was not completed and
ProcessCompletedwas never received. - The user was interrupted by a recoverable document, selfie, or video error.
When requesting recovery:
- if the validation is still active, a new token can be generated and the user can continue from the interruption point
- if the validation has expired or is no longer recoverable, a new onboarding must be started
Integrators should:
- monitor completion events
- track unfinished validations
- handle backend response codes explicitly
- inform the user when recovery is not possible
Example: Module integration¶
import UIKit
import VeridasXpressID
final class OnboardingViewController: UIViewController {
let XPRESSID_URL = "Your XpressID_URL"
let ACCESS_TOKEN = "Your Access_Token"
func startXpressID() {
XpressID.setEventsDelegate(delegate: self)
XpressID.start(
delegate: self,
baseUrl: XPRESSID_URL,
accessToken: ACCESS_TOKEN
)
}
}
extension OnboardingViewController: XpressIDDelegate {
func onXpressIDStarted() {
print("XpressIDResult -> Started")
}
func onXpressIDFinished(error: XpressIDError?) {
print("XpressIDResult -> Finished: \(error == nil)")
if let error {
print("XpressIDError: \(error)")
dismiss(animated: false)
}
}
}
extension OnboardingViewController: XpressIDEventsDelegate {
func onXpressIDEvent(event: XpressIDEvent) {
print("Event received: \(event)")
}
}