Holder SDK quickstart guide
This quickstart is for evaluating the MATTR Pi mDocs Holder SDKs. In about 15-20 minutes you will configure a sample holder mobile app (iOS, Android, or React Native), run it on a physical device and use it to:
- Claim a credential issued through an OID4VCI workflow.
- Present that credential remotely to a web verifier application.
- Present that credential in-person to a verifier application on a different mobile device.
Estimated time: 15-20 minutes.
Use this guide as a fast evaluation path to see the Holder SDK working end-to-end on a real device. For detailed information and API examples, explore the individual tutorials and reference documentation for credential claiming, remote presentation, and proximity presentation.
Prerequisites
- Access to the MATTR Pi mDocs Holder SDK for your chosen platform (iOS, Android, or React Native). Apply for access here.
- A physical mobile device to run the holder application:
- iOS/Android mobile device set up with biometric authentication and Bluetooth access.
- Development environment set up for your chosen platform (e.g., Xcode for iOS, Android Studio for Android, or a React Native development environment).
- Install the MATTR GO Verify example app on a separate mobile device for iOS or Android. This is used for in-person verification testing.
Evaluation Steps
In this quickstart you will:
- Configure and run a local sample holder project for your platform.
- Claim a credential into the sample holder app.
- Present that credential remotely to a web verifier.
- Present that credential in-person to a verifier app on a different device.
Use the tabs below to follow platform-specific setup steps.
Part 1: Configure the sample holder project (5-10 minutes)
- Download and unzip the sample iOS holder project.
- Use Xcode to open the
.xcodeprojfile in the unzipped folder. - Drag the
MobileCredentialHolderSDK-*version*.xcframeworkfolder (obtained from MATTR as part of the SDK package) into your project. - Configure
MobileCredentialHolderSDK-*version*.xcframeworkto Embed & sign. - Set a unique bundle identifier for the project in the Xcode project settings (e.g.,
com.yourname.holdersample). - Run the project on a physical iOS device (simulators do not support the required Bluetooth capabilities).
- Download and unzip the sample Android holder project.
- Open the project in Android Studio.
- Unzip the
mobile-credential-holder-*version*.zipfile (obtained from MATTR as part of the SDK package). - Drag the unzipped
globalfolder into the project'srepofolder. - Sync Project with Gradle files to recognize the new module.
- Run the project on a physical Android device (emulators do not support the required Bluetooth capabilities).
-
Access the sample holder codebase by either:
-
Cloning the MATTR Sample Apps repository:
Clone sample apps git clone https://github.com/mattrglobal/sample-apps.git -
Or downloading just the holder sample using download-directory.github.io.
-
-
Open the completed sample holder project in your code editor. You can find it in the
sample-apps/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-completedirectory. -
Open the
app.config.tsfile. -
Update the iOS
bundleIdentifierto a unique value for your application, e.g.com.mycompany.myapp. -
Update the Android
packageto a unique value, e.g.com.mycompany.myapp. -
Navigate to the project directory and install dependencies:
Install dependencies yarn install
You must be logged in to npm with access to the MATTR Pi mDocs Holder SDK package to install dependencies successfully. If you have not yet been granted access to the SDK, apply for access here.
-
Run the following command to generate the iOS and Android project files:
Generate native project files npx expo prebuildYou should now see the
iosandandroidfolders in your project root. -
Connect your testing device and run the following command to start the application:
iOS
Run iOS application yarn ios --deviceAndroid
Run Android application yarn android --device
Part 2: Claim a credential (3 minutes)
Use the sample holder app you just built to claim a credential from a MATTR VII tenant. This credential will be used in the subsequent presentation steps.
- Open the MATTR Labs Pre-authorized Offer tool.
- Select the Generate Credential Offer button.
A QR code will be generated and rendered on the screen, along with a transaction code. - Run your application.
- Select the Claim Credential button.
- Scan the QR code.
- Follow any on-screen instructions to claim the credential.
Under the hood, the sample app uses the Holder SDK to discover the credential offer from the QR code, and then claim and store the credential on the device using the OID4VCI Pre-authorized Code flow.
Part 3: Present the credential remotely (5 minutes)
Present the credential you just claimed to a web verifier application using the remote presentation workflow defined by ISO/IEC 18013-7:2025 and OID4VP.
- Use a mobile browser on the device where the sample application is installed to navigate to the MATTR Labs remote presentation testing tool.
- Select the OID4VP (Redirect) option from the Select Experience list.
- Select Request credentials.
- Select Allow to open the sample application.
- Select the credential you claimed in the previous part.
- Select Send Response.
- You should be redirected back to the MATTR Labs remote presentation testing tool, where you will see a successful verification indication.
- Use a desktop browser to navigate to the MATTR Labs remote presentation testing tool.
- Select OID4VP (Redirect) from the Select Experience list.
- Select Request credentials.
- Open the camera on the device where the sample application is installed and scan the QR code.
- Confirm opening the QR code with your sample application (this message might vary depending on your device and platform).
- Select the credential you claimed in the previous part.
- Select Send Response.
- Back on your desktop browser, you should see a successful verification indication.
In these flows, the sample app uses the Holder SDK to respond to the OID4VP authorization request, as defined in the OID4VP specification and ISO/IEC 18013-7:2025.
Part 4: Present the credential in-person (3 minutes)
Present the credential you claimed to a verifier application on a different mobile device using the proximity presentation workflow as defined by ISO/IEC 18013-5:2021.
Once you have the sample holder app running on one device, and the MATTR GO Verify app installed on a separate device, follow these steps to test in-person presentation:
- On the sample holder app, select Present Credentials.
- The app will display a QR code containing the device engagement string.
- On the verifier device, launch the MATTR GO Verify app, select Scan and scan the QR code from the holder app.
- On the sample holder app, review the verifier's request and consent to share the credential.
- The verifier application will display the verification results.
Behind the scenes, the Holder SDK handled device engagement, established a secure BLE session, and presented the mDoc to the verifier as per ISO/IEC 18013-5:2021.
Review the codebase
The sample holder application uses the Holder SDK to claim and present mDocs. Once you have the sample application running, use this section to understand the key SDK calls you will reuse in your own application.
Initialize the SDK
try mobileCredentialHolder.initialize(
userAuthenticationConfiguration: UserAuthenticationConfiguration(
userAuthenticationBehavior: .always,
userAuthenticationType: .biometricOrPasscode,
presentationTimeoutSeconds: 20
),
credentialIssuanceConfiguration: CredentialIssuanceConfiguration(
redirectUri: "<WalletRedirectUri>",
autoTrustMobileCredentialIaca: true
),
dcConfiguration: DCConfiguration( // iOS 26+ only
appGroup: "group.com.yourcompany.app",
supportedDocTypes: [.mDL, .eudi]
)
)userAuthenticationConfiguration: Optional. Configures when the SDK requires user authentication to access or present credentials. The example shows the default values. Refer to the reference documentation for additional details about the available configuration options.redirectUri: Provide a URL that corresponds to a specific path in your application. The SDK will redirect the user to this path after they successfully complete authentication. Required for theAuthorization Code flow.autoTrustMobileCredentialIaca: Set totrueif you trust the credential issuer(s) for any offer. Defaults tofalse.dcConfiguration: Optional, iOS 26+ only. Configures Digital Credentials API support for presenting credentials via an OS-native overlay. Specify your app group and supported document types (e.g.,.mDL,.eudi,.euav,.photoid,.jpMnc).
mobileCredentialHolder.initialize(
activity,
userAuthenticationConfiguration = UserAuthenticationConfiguration(
behavior = UserAuthenticationBehavior.Always,
presentationTimeoutSeconds = 20
),
credentialIssuanceConfiguration = CredentialIssuanceConfiguration(
redirectUri = "<WalletRedirectUri>",
autoTrustMobileCredentialIaca = true
)
)userAuthenticationConfiguration: Optional. Configures when the SDK requires user authentication to access or present credentials. The example shows the default values. Refer to the reference documentation for additional details about the available configuration options.redirectUri: Provide a URL that corresponds to a specific path in your application. The SDK will redirect the user to this path after they successfully complete authentication. Required for theAuthorization Code flow.autoTrustMobileCredentialIaca: Set totrueif you trust the credential issuer(s) for any offer. Defaults tofalse.
const initializeResult = await mobileCredentialHolder.initialize({
userAuthenticationConfiguration: {
userAuthenticationBehavior: UserAuthenticationBehavior.OnInitialize,
userAuthenticationType: UserAuthenticationType.BiometricOrPasscode,
},
credentialIssuanceConfiguration: {
autoTrustMobileCredentialIaca: true,
redirectUri: "<WalletRedirectUri>",
},
});
if (initializeResult.isErr()) {
const { error } = initializeResult;
// handle error scenarios
return;
}userAuthenticationConfiguration: Optional. Configures when the SDK requires user authentication to access or present credentials. Refer to the reference documentation for additional details about the available configuration options.redirectUri: Provide a URL that corresponds to a specific path in your application. The SDK will redirect the user to this path after they successfully complete authentication. Required for theAuthorization Code flow.autoTrustMobileCredentialIaca: Set totrueif you trust the credential issuer(s) for any offer. Defaults tofalse.
Credential claiming workflow
Discover credential offer
let discoveredOffer = try await mobileCredentialHolder.discoverCredentialOffer(offerUrl: String)offerUrl: Pass the OID4VCI initiation URL.
The
discoverCredentialOffer
method returns a
DiscoveredCredentialOffer
object containing the offer details.
val discoveredOffer = mobileCredentialHolder.discoverCredentialOffer(offerUri)offerUrl: Pass the OID4VCI initiation URL as a string.
The
discoverCredentialOffer
method returns an instance of
DiscoveredCredentialOffer
containing the offer details.
const discoveredOfferResult =
await mobileCredentialHolder.discoverCredentialOffer(offerUrl);
if (discoveredOfferResult.isErr()) {
const { error } = discoveredOfferResult;
// handle error scenarios
return;
}
const credentialOffer = discoveredOfferResult.value;offerUrl: Pass the OID4VCI initiation URL.
The discoverCredentialOffer method returns a Result<CredentialOfferResponse, Error> (using the
neverthrow pattern) object containing the offer
details and/or any errors.
Claim and store credentials
let retrievedCredentialResults = try await mobileCredentialHolder.retrieveCredentials(
credentialOffer: String,
clientId: "<WalletClientID>",
transactionCode: nil
)credentialOffer: Pass the OID4VCI credential offer URI.clientId: Pass a string that identifies your wallet application with the issuer.transactionCode: Provide a transaction code if thePre-Authorization Code flowoffer requires a transaction code.
The
retrieveCredentials
method returns an array of
RetrieveCredentialResult
objects, each containing the metadata of the retrieved credentials, including the credential ID
(credentialId).
val credentialResults = mobileCredentialHolder.retrieveCredentials(
activity = activity,
credentialOffer = offerUri,
clientId = "<WalletClientID>",
transactionCode = null
)credentialOffer: Pass the OID4VCI credential offer URI.clientId: Pass a string that identifies your wallet application with the issuer.transactionCode: Provide a transaction code if claiming a credential using aPre-authorized Code flowthat requires a transaction code.
The
retrieveCredentials
method returns an array of
RetrieveCredentialResult
objects, each containing the metadata of the retrieved credentials, including the credential ID
(credentialId).
const retrievedCredentialsResults =
await mobileCredentialHolder.retrieveCredentials({
credentialOffer: offerUrl,
clientId: "<WalletClientID>",
transactionCode: undefined,
});
if (retrievedCredentialsResults.isErr()) {
const { error } = retrievedCredentialsResults;
// handle error scenarios
return;
}
const retrievedCredentials = retrievedCredentialsResults.value;
const retrievedCredential = retrievedCredentials[0];
if (retrievedCredential?.error || !retrievedCredential?.credentialId) {
// handle error scenarios
return;
}
const credentialId = retrievedCredential.credentialId;credentialOffer: Pass the OID4VCI credential offer URI.clientId: Pass a string that identifies your wallet application with the issuer.transactionCode: Provide a transaction code if thePre-Authorization Code flowoffer requires a transaction code.
The retrieveCredentials method returns a Result<RetrieveCredentialsResponse, Error> (using the
neverthrow pattern) object containing the metadata of
the retrieved credentials, including the credential ID (credentialId).
Access claimed credentials
let credential = try mobileCredentialHolder.getCredential(credentialId: String)credentialId: Pass the credential ID of the credential you want to access.
val credential = mobileCredentialHolder.getCredential(credentialId)credentialId: Pass the credential ID of the credential you want to access, as a string.
const credentialResult =
await mobileCredentialHolder.getCredential(credentialId);
if (credentialResult.isErr()) {
const { error } = credentialResult;
// handle error scenarios
return;
}
const credential = credentialResult.value;credentialId: Pass the credential ID of the credential you want to access.
Remote presentation handling
Register the verifier's Authorization URL
Ensure that your application is registered to
handle the verifier's authorization URL.
This is typically done in the app's Info.plist file. The URL scheme should match the one used by
the verifier.
Ensure that your application is registered to handle the verifier's authorization URL. This is typically done in the AndroidManifest.xml file. The URL scheme should match the one used by the verifier.
Create an online presentation session
let onlinePresentationSession = try await mobileCredentialHolder.createOnlinePresentationSession(authorizationRequestUri: authorizationRequestURI, requireTrustedVerifier: false)authorizationRequestURI: Pass the OID4VP authorization request URI as a string.requireTrustedVerifier: Set totrueif you want to ensure that the verifier exists on the app's trusted verifier's list.
The
createOnlinePresentationSession
method returns an instance of
OnlinePresentationSession
containing requested and matching credentials.
val onlinePresentationSession = mobileCredentialHolder.createOnlinePresentationSession(
authorisationRequestUri = authorizationRequestUri,
requireTrustedVerifier = false
)authorisationRequestURI: Pass the OID4VP authorization request URI as a string.requireTrustedVerifier: Set totrueif you want to ensure that the verifier exists on the app's trusted verifier's list.
The
createOnlinePresentationSession
method returns an instance of
OnlinePresentationSession
containing requested and matching credentials.
const createSessionResult =
await mobileCredentialHolder.createOnlinePresentationSession({
authorizationRequestUri: authorizationRequestURI,
requireTrustedVerifier: false,
});
if (createSessionResult.isErr()) {
const { error } = createSessionResult;
// handle error scenarios
return;
}
const onlinePresentationSession = createSessionResult.value;authorizationRequestUri: Pass the OID4VP authorization request URI as a string.requireTrustedVerifier: Set totrueif you want to ensure that the verifier exists on the app's trusted verifier's list.
The createOnlinePresentationSession method returns an instance of OnlinePresentationSession
containing requested and matching credentials.
Present matching credentials to holder
let credential = try await mobileCredentialHolder.getCredential(credentialId: id)id: Pass the identifier of the credential you want to present, as a string. This information can be retrieved from theOnlinePresentationSessionobject returned by thecreateOnlinePresentationSessionmethod.
Your application must then gather the user's consent to share the matching credential with the verifier.
val credential = mobileCredentialHolder.getCredential(credentialId)credentialId: Pass the identifier of the credential you want to present, as a string. This information can be retrieved from theOnlinePresentationSessionobject returned by thecreateOnlinePresentationSessionmethod.
Your application must then gather the user's consent to share the matching credential with the verifier.
const credentialResult = await mobileCredentialHolder.getCredential(
credentialId,
);
if (credentialResult.isErr()) {
const { error } = credentialResult;
// handle error scenarios
return;
}
const credential = credentialResult.value;credentialId: Pass the identifier of the credential you want to present, as a string. This information can be retrieved from theOnlinePresentationSessionobject returned by the method.
Your application must then gather the user's consent to share the matching credential with the verifier.
Send a response to the verifier
try await onlinePresentationSession.sendResponse(credentialIds: [id])id: Pass the identifier of the credential you want to send, as a string.
onlinePresentationSession.sendResponse(
credentialIds = listOf(credentialId),
activity = activity
)credentialId: Pass the identifier of the credential you want to send, as a string.
const sendResponseResult = await onlinePresentationSession.sendResponse({
credentialIds: [credentialId],
});
if (sendResponseResult.isErr()) {
const { error } = sendResponseResult;
// handle error scenarios
return;
}credentialId: Pass the identifier of the credential you want to send, as a string.
Proximity presentation handling
Create a proximity presentation session
let proximityPresentationSession = try await mobileCredentialHolder.createProximityPresentationSession(
onRequestReceived: onRequestReceived(_:error:)
// 1. Callback triggered when the verifier requests a credential for verification. Use this callback to display the request on the UI and get the user's consent to share any matched credentials.
)The
createProximityPresentationSession
method returns an instance of
ProximityPresentationSession
which contains a deviceEngagement property that must be shared with the verifier embedded in a QR
code to establish a secure connection.
val proximityPresentationSession = mobileCredentialHolder.createProximityPresentationSession(
onRequestReceived = { session, matchedCredentials, error ->
if (error != null) {
// Handle error
} else {
// 1. Show request on UI
// 2. Show matched credentials on UI and get user's consent to share them
// 3:
session.sendResponse(
val credentialIds = matchedCredentials!!.flatMap {
it.matchedCredentials.map { credential -> credential.id }
} ,
activity = activity
)
}
}
)The
createProximityPresentationSession
method returns an instance of
ProximityPresentationSession
which contains a deviceEngagement property that must be shared with the verifier embedded in a QR
code to establish a secure connection.
const createSessionResult =
await mobileCredentialHolder.createProximityPresentationSession({
onRequestReceived: (data) => {
if ("error" in data) {
// handle error scenarios
return;
}
// 1. Show UI prompt and gather user consent
// 2. Retrieve matching credential from request object
},
});
if (createSessionResult.isErr()) {
const { error } = createSessionResult;
// handle error scenarios
return;
}
const proximityPresentationSession = createSessionResult.value;The createProximityPresentationSession method returns an instance of
ProximityPresentationSession which contains a deviceEngagement property that must be shared with
the verifier embedded in a QR code to
establish a secure connection.
Present matching credentials to holder
let credential = try await mobileCredentialHolder.getCredential(credentialId: id)id: Pass the identifier of the credential you want to present, as a string. This information is returned by thecreateProximityPresentationSessionmethod on aonRequestReceivedevent, triggered when the verifier requests a credential for verification.
Your application must then gather the user's consent to share the matching credential with the verifier.
val credential = mobileCredentialHolder.getCredential(credentialId)credentialId: Pass the identifier of the credential you want to present, as a string. This information is returned by thecreateProximityPresentationSessionmethod on aonRequestReceivedevent, triggered when the verifier requests a credential for verification.
Your application must then gather the user's consent to share the matching credential with the verifier.
const credentialResult =
await mobileCredentialHolder.getCredential(credentialId);
if (credentialResult.isErr()) {
const { error } = credentialResult;
// handle error scenarios
return;
}
const credential = credentialResult.value;
//
// Send a response to the verifier
//
const sendResponseResult =
await mobileCredentialHolder.sendProximityPresentationResponse({
credentialIds: [credentialId],
terminateSession: false,
});
if (sendResponseResult.isErr()) {
const { error } = sendResponseResult;
// handle error scenarios
return;
}credentialId: Pass the identifier of the credential you want to present, as a string. This information is returned by thecreateProximityPresentationSessionmethod on aonRequestReceivedevent, triggered when the verifier requests a credential for verification.
Your application must then gather the user's consent to share the matching credential with the verifier.
Send a response to the verifier
try await proximityPresentationSession.sendResponse(credentialIds: [id])id: Pass the identifier of the credential you want to send, as a string.
proximityPresentationSession?.sendResponse(credentialIds = listOf(credentialId))credentialId: Pass the identifier of the credential you want to send, as a string.
const sendResponseResult =
await mobileCredentialHolder.sendProximityPresentationResponse({
credentialIds: [credentialId],
});
if (sendResponseResult.isErr()) {
const { error } = sendResponseResult;
// handle error scenarios
return;
}credentialId: Pass the identifier of the credential you want to send, as a string.
Next steps
- Explore the individual tutorials for detailed step-by-step instructions:
- Review the mDocs Holder SDK overview for platform support and capabilities.
- Use the sample app to claim a credential via an Authorization Code flow to experience this flow.
How would you rate this page?
Last updated on