> For the complete documentation index, see [llms.txt](https://docs.indicio.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.indicio.tech/developer/mobile-solutions/holdr+/holdr-integration-tutorial.md).

# Holdr Integration Tutorial

The Indicio Holdr SDK (Software Development Kit) supports Android and iOS platforms and provides holder capabilities that are compatible with many Aries protocols. Consumption of the Holdr SDK will allow an application to have its own Aries Askar wallet for holding digital credentials that conform to the Anoncreds specification, communicate with ledgers through IndyVDR, and generate zero trust proofs for information verification through Anoncreds-RS.

## Adding the Holdr SDK to Your Project

### iOS

1. Open your iOS project in XCode and click on your project name under the “TARGETS” section.
2. Scroll down in the “General” tab until you see the “Frameworks, Libraries, and Embedded Content” section of the project.
3. Click on the **+** button under “Frameworks, Libraries, and Embedded Content.”
4. Select “Add Files…” in the bottom left dropdown and select “Proven SDK XCFramework” to add it to your project.

### Android

{% hint style="info" %}
We recommend you add Github Authentication to `local.properties` or other secrets file.
{% endhint %}

To consume the Holdr SDK in Android, you must have access to Maven repos hosted on GitHub. For this you must add your GitHub username and a GitHub personal access token with read permissions to your `local.properties`. You will access those values in your `build.gradle`.<br>

Information for creating a personal access token can be [found here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).

Official documentation for reading values from the `local.properties` can be [found here](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry#example-using-gradle-groovy-for-a-single-package-in-a-repository).

#### Android Installation

1. Add the below repos to your Android repository list.

```
// Example function to read values from local.properties in a build.gradle.kts file
fun readLocalProperty(key: String): String? {
    val localPropertiesFile = File(rootDir, "local.properties")
    if (localPropertiesFile.exists()) {
        val properties = Properties()
        localPropertiesFile.inputStream().use { properties.load(it) }
        return properties.getProperty(key)
    }
    return null
}

repositories {
    maven {
        setUrl("https://maven.pkg.github.com/indicio-tech/holdr-sdk-release")
        credentials {
            username = readLocalProperty("githubUsername")
            password = readLocalProperty("githubToken")
        }
    }
    maven {
        setUrl("https://maven.pkg.github.com/LF-Decentralized-Trust-labs/aries-uniffi-wrappers")
        credentials {
            username = readLocalProperty("githubUsername")
            password = readLocalProperty("githubToken")
        }
    }
}

```

2. Add the following implementation to the project dependencies in the `build.gradle.kts` file.

```
dependencies {
    implementation("tech.indicio:holdr:1.1")
}
```

3. Make sure your AndroidManifest.xml file includes the following permissions.

```
<manifest>
    <uses-permission android:name="android.permission.  READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.  WRITE_EXTERNAL_STORAGE" />
        <!-- For Android 10 (API level 29) and above -->
    <uses-permission android:name="android.permission.  MANAGE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>
```

### React Native

#### Installation

You will want to add `@holdr/core` and `@holdr/react-native` to your project.

1. Place `holdr-core-v1.1.0.tgz` and `holdr-react-native-v1.1.0.tgz` in a directory parallel to your `package.json`.<br>
2. In `package.json`, add them both as dependencies.

```
"dependencies": {
    "@holdr/react-native": "file:holdr-react-native-v1.1.0.tgz",
    "@holdr/core": "file:holdr-core-v1.1.0.tgz",
}

```

3. Call `yarn install`.

#### Android

1. Add repositories to `android/build.gradle` and confirm `minSdkVersion` is greater than or equal to 24.

```
buildscript {
    ext {
        minSdkVersion = 24
    }
}   
allprojects{
    repositories{
        maven {
            setUrl("https://maven.pkg.github.com/indicio-tech/holdr-sdk-release")
            credentials {
                username = readLocalProperty("githubUsername")
                password = readLocalProperty("githubToken")
            }
        }
        maven {
            setUrl("https://maven.pkg.github.com/LF-Decentralized-Trust-labs/aries-uniffi-wrappers")
            credentials {
                username = readLocalProperty("githubUsername")
                password = readLocalProperty("githubToken")
            }
        }
    }
}
```

\
Manually Link Modules (If They Are Not Getting Automatically Linked)

Older versions of React Native (0.67.5) may have trouble automatically linking the native modules. If you run into issues, we recommend linking the package manually. Follow the instructions for either iOS or Android:

#### iOS

1. Add the pod to `ios/Podfile`.

```
target 'ExampleApp' do
    <!-- Other configs -->
+   pod 'rtn-proven-mobile-sdk', :path => '../node_modules/@holdr/react-native'

    target 'ExampleAppTests' do

        <!-- Other configs -->
```

2. Reinstall the pods.

#### Android

1. Add the project to `android/settings.gradle`.

```
<!-- Other configs -->
+include ':holdr-react-native
+project(':holdr-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/@holdr/react-native/android')

```

2. Add module to dependency in `android/app/build.gradle`.

```
dependencies{
    <!-- Other dependencies -->
+    implementation project(":holdr-react-native")
}

// If you have other libraries that use libc++_shared.so or libfbjni.so
// you may need to add the following to your android configs
android{
    <!-- Other configs -->
+    packagingOptions {
+        pickFirst '**/libc++_shared.so'
+        pickFirst '**/libfbjni.so'
+    }
}

```

3. Import and link package in `android/app/src/main/java/com/example/MainApplication.java`.

```
+ import com.rtnprovenmobilesdk.ProvenMobilePackage;

public class MainApplication extends Application implements ReactApplication {
   
    private final ReactNativeHost mReactNativeHost =
        new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }
        @Override
        protected List<ReactPackage> getPackages() {
        @SuppressWarnings("UnnecessaryLocalVariable")
        List<ReactPackage> packages = new PackageList(this).getPackages();
+       packages.add(new ProvenMobilePackage());
        return packages;
        }

```

4. Older React Native versions (0.67.5) may need to upgrade their gradle wrapper to 7.4 (declared in `android/gradle/wrapper/gradle-wrapper.properties`) and upgrade `com.android.tools.build:gradle` (declared in `android/build.gradle`) to 7.3.1.

## Creating an Agent

### Wallet Config

To create an Agent, you need to first create a wallet config object to tell the Agent how to initialize your wallet.

#### Kotlin

```
val walletConfig: WalletConfig = WalletConfig(
    uri = "sqlite://pathWhereYouWantDataStored/local.db",
    // options are raw(raw random key), kdf:argon2i(Encrypted Key), none(testing only)
    keyMethod = "raw",
    // Should be randomly generated or derived from a pin or password, must be able to be repeatedly accessed
    passkey = "TheActualKeyThatUnlocksTheWallet",
    id = "Unique id for this wallet"
)
```

#### Swift

```
let walletConfig = WalletConfig(
    uri: "sqlite://pathWhereYouWantDataStored/local.db",
    keyMethod: "raw",
    passkey: "TheActualKeyThatUnlocksTheWallet",
    id: "Unique id for this wallet"
)
```

### Mediation Config

{% hint style="info" %}
If you are not using DIDcomm, you can pass `null` for the mediation config because you do not need a mediator.
{% endhint %}

Next create a mediation config to pass to our Agent. The reconnection interval in the below section has two values that indicate the minimum and maximum amount of time in a back-off strategy on which to try to contact the mediator.

#### Kotlin

```
val mediationConfig: MediationConfig = MediationConfig(  
    mediatorInvitationUrl = "Mediation connection url for the default mediator you want to use",
    // Optional parameter to indicate how often, in milliseconds,the Agent should try to reconnect to the mediator
    baseMediatorReconnectionIntervalMS = 500,
    // Optional parameter to indicate the max amount of time between attempts to contact the mediator
    maximumMediatorReconnectionIntervalMS: Int = 10000
)
```

#### Swift

```
let mediationConfig = MediationConfig(
    mediatorInvitationUrl: "Mediation connection URL for the default mediator you want to use",
    // Optional parameter to indicate how often, in milliseconds, the Agent should try to reconnect to the mediator
    baseMediatorReconnectionIntervalMS: 500,
    // Optional parameter to indicate the max amount of time between attempts to contact the mediator
    maximumMediatorReconnectionIntervalMS: 10000
)
```

### Pool Config

The pool config is used to indicate what networks or ledgers the Agent can read from. Not all ledgers contain all schemas or credential definitions, and some are more volatile than others. The standard way to declare a pool config is shown in the examples below and again the React Native portion of this document.

The first value used to create a pool config is the `genesisUrl`. This is a URL point to the genesis file in a raw string format. This file is used to establish connection and provide data about the network or ledger.

The second value is the `isProduction` Boolean flag. This indicates to the agent whether to treat the network/ledger as a production environment or not.<br>

The last value needed is the `indyNamespace`. This string value is the name by which the network or ledger is known and is prepended to schemas and credential definitions to identify what network or ledger they are on. Misspelling this value or having an incorrect variation will cause errors when reading from the ledger.

#### Kotlin

```
  val indicioDemoNet = IndyVDRPoolConfig.fromUrl(
        genesisUrl = "https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_demonet_genesis",
        isProduction = false,
        indyNamespace = "indicio-demo-net"
    )
```

#### Swift &#x20;

```
let indicioDemoNet = IndyVDRPoolConfig.companion.fromUrl(
        genesisUrl = "https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_demonet_genesis",
        isProduction = false,
        indyNamespace = "indicio-demo-net"
    )
```

### Configuring an Agent

#### Kotlin

```
val agent: Agent = Agent(
    license: licenseString, // SDK license as a string
    walletConfig = walletConfig,
    mediationConfig = mediationConfig, // Set to null if you do not need didcomm
    pools = ListOf(
        indicioDemoNet
    ),
    pickupBatchSize = 10, // max number of message that can be picked up at once from the mediator
    defaultConnectionLabel = "Proven SDK", // The label the Agent will use in didComm communication
    enableLogging = false,
    autoAcceptConnections = true,
    autoAcceptCredentials = false,
    customLogger = null // (message: String)->Unit Callback to use for logging instead of `println()`
)
```

#### Swift

```
let agent = Agent(
    license: licenseString, // SDK license as a string
    walletConfig: walletConfig,
    mediationConfig: mediationConfig, // Set to nil if not using didcomm
    pools: [
        indicioDemoNet
    ],
    pickupBatchSize: 10, // max number of message that can be picked up at once from the mediator
    defaultConnectionLabel: "Proven SDK", // The label the Agent will use in didComm communication
    enableLogging: false,
    autoAcceptConnections: true,
    autoAcceptCredentials: false,
    customLogger: nil // (message: String)->Void Callback to use for logging instead of `println()`
)

// Start agent (KMPNativeCoroutines)
try await asyncFunction(for: agent.start(startUpTimeout: 10000))

// Start agent (Completion handler)
agent.start(startUpTimeout: 10000){error in
    if(error != nil){
        // Handle error
    }
    // Code to be called when agent started
}

// Start agent (Without KMPNativeCoroutines)
let error: ProvenError? = await agent.start(startUpTimeout: 10000)
if(error != nil){
    // Handle error
}

```

Once the Agent is created and configured, you will have to call `agent.start()` before you can use the agent for anything. When the app is closing, `agent.stop()` should be called to safely shut the agent down between sessions.

If you want to remove the Agent or reset the wallet you can call `agent.delete()` to remove all data the agent has saved.

### React Native

Creating an Agent follows similar steps as Kotlin and Swift but has some additional steps specific to React Native.

Similar to Kotlin and Swift, some config objects must be created to customize the Agent. Additionally, to create a pool config from a URL pointing to genesis files, you must pass the `ProvenReactNative` object imported from the `@holdr/react-native` package.

```
import RNFS from 'react-native-fs';
import ProvenReactNative, {
    provenEventsFactory,
} from '@holdr/react-native';
import {
    Agent,
    IndyPoolConfig,
    type MediationConfig,
    type WalletConfig
} from '@holdr/core';

const walletConfig: WalletConfig = {
    uri: `sqlite://${RNFS.DocumentDirectoryPath}/local.db`,
    keyMethod: 'raw',
    passkey: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb',
    id: 'ReactNativeTest',
    profile: 'test',
};

const mediationConfig: MediationConfig = {
    mediatorInvitationUrl:
    'url invite to the mediator you want to use',
    baseMediatorReconnectionIntervalMS: 50,
    maximumMediatorReconnectionIntervalMS: 10000,
};

const poolConfig: IndyVDRPoolConfig = await IndyPoolConfig.fromURL(
    ProvenReactNative,
    'https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_demonet_genesis',
    false,
    'indicio-demo-net',
);
```

The actual Agent is created by passing the created config objects along with some other options. Again, the `ProvenReactNative` object must be included in the Agent constructor along with the `provenEventsFactory` that facilitates getting events from the Agent to the React Native level.

```
import ProvenReactNative, {
    provenEventsFactory,
} from '@holdr/react-native';

const agent = new Agent(
    ProvenReactNative,
    provenEventsFactory,
    licenseString,
    walletConfig,
    mediationConfig,
    [poolConfig],
    'RN',
    10,
    true,
    true,
    true,
    console.info // (message: string)->Void Callback to use for logging, defaults to console.log
);
```

Once the Agent has been constructed you can start the Agent with the following code. The React Native code allows the use of the Javascript Promise syntax for async operations.

```
await agent.start();
```

Similarly, the `agent.stop()` and `agent.delete()` functions exist on the agent object along with access to all other modules and their respective functions mirroring the Kotlin and Swift implementations.

## Events

Events are handled as flows in Kotlin and can be retrieved from the agent.

{% hint style="info" %}
The following examples of event flows do not include the code needed for launching them in a background coroutine scope. That code needs to be added in your Kotlin code to take the handlers off of the main thread.
{% endhint %}

Kotlin

```
val didExchangeEvents? = agent.events.getEventBus(DidExchangeEvents::class)
// or
val didExchangeEvents = agent.events.getDidExchangeEvents()?: throw Error("Events not initialized by agent")
```

The `getEventBus` function will attempt to retrieve the event bus class passed to it. This exists because custom event flows can be registered and stored in the `agent.events eventManager` object. There are eight base flows for different actions that the Agent manages, their functions are as follows:&#x20;

* `getDidExchangeEvents()`
* `getAgentEvents()`
* `getCredentialEvents`
* `getEventBusEvents()`
* `getMessageEvents()`
* `getProofEvents()`
* `getTrustPingEvents()`
* `getWebsocketEvents()`

All of the listed functions exist on the `events` property of the agent. The event bus events pertain to record updates. The message events pertain to all incoming messages regardless of the type. Agent events are emitted to indicate the state (Start, Running, Stop) of the Agent.

Both the named functions and the `getEventBus` may return `null` if, for some reason, the event bus has not yet been registered to the agent's `eventManager`. The named function returns `null` only if the Agent initializes with errors.

Once you have the events, you can perform operations on them to filter for certain IDs or states to complete processes or inform the user when things are occurring.

{% hint style="info" %}
Performing certain actions that wait for completion of a protocol inside of an event handler can cause a deadlock situation. It is not advised to wait for protocol completion inside a continuous event handler.
{% endhint %}

```
  // Example of what NOT to do
  val didExchangeEvents = agent.events.getDidExchangeEvents()?: throw Error("Events not initialized by agent")

  didExchangeEvents.events.onEach{
      // Causes deadlock because request connection will not return until the response message is processed
      // This can be avoided by calling this function in a separate thread
      agent.didExchange.requestConnection(it.didExchangeRecord.id, it.outOfBandRecord.id)
  }.collect()

  // Auto accept should be used instead of trying to do this. This is an intentionally BAD example

```

If, for some reason, you need to wait for the agent to complete an action you should use the Kotlin or Swift (using KMPN NativeCoroutines) directions following this paragraph.

#### Kotlin

```
val didExchangeEvents = agent.events.getDidExchangeEvents()?: throw Error("Events not initialized by agent")
// Waits for the first didExchangeStateChangedEvent to be emitted that is in the completed state
didExchangeEvents.events.first{
  it.didExchangeRecord.state == DidExchangeState.Completed
}
// Additional attributes (such as ID) on the record can be used to wait for a specific record to reach a certain state
```

In Kotlin the events use flows, this allows the use of the [Kotlin Asynchronous Flow](https://kotlinlang.org/docs/flow.html) on all events.

#### Swift (Using KMPNativeCoroutines)

Swift uses [KMPNativeCoroutines](https://github.com/rickclephas/KMP-NativeCoroutines) (1.0.0-ALPHA-23) as a dependency that allows the Kotlin flows to be turned into native Swift observables, `asyncSequences` and potentially more.

```
// Turns the didExchangeEvents into a swift asyncSequence
let credentialEvents = asyncSequence(for: agent.events.getCredentialEvents()!.events)
```

#### Swift/Objective-C (Without KMPNativeCoroutines)

We also wrapped events with the following methods so you can easily listen to events without a third party library or with Objective-C:

* `onDidExchangeStateChanged`
* `onCredentialStateChanged`
* `onTrustPingEvent`
* `onAgentEvent`
* `onRecordEvent`
* `onProofEvent`
* `onWebsocketEvent`

**Objective-C**

```
void (^removeListener)(ProvenmobileKotlinCancellationException * _Nullable) = 
  [agent.events onDidExchangeStateChangedCallback:^(
    ProvenmobileDidExchangeStateChangedEvent *_Nonnull event
  ) {
    // Handle DidExchangeStateChangedEvent here
  }

// Remove listener when no longer needed
removeListener(nil);
```

**Swift**

```
let removeListener = agent.events.onDidExchangeStateChanged { event in
  // Handle DidExchangeStateChangedEvent here
}
// Remove listener when no longer needed
removeListener(nil)
```

#### React Native

Events in React Native are handled differently than in Kotlin and Swift. The format of events on the Agent is similar but is done in a simpler manner.

Instead of getting an events object, register a handler function that will be called on all events of the specified type.

```
// Registers a handler that is called on all Did Exchange events
const didExchangeRemove = agent.events.registerDidExchangeHandler(
    event => {
      if (event.didExchangeRecord.state === DidExchangeState.COMPLETED) {
        console.log(
          'didExchange completed',
          event.didExchangeRecord.theirLabel,
        )
      }
    })

didExchangeRemove()
```

The return of registering a handler is a function that will remove or unregister the handler. Calling the removal function stops the given handler from being called on any future events.

{% hint style="info" %}
Registered events will not persist after the app has been closed.
{% endhint %}

### Creating a Connection

All connections are made through the out-of-band protocol using the out-of-band module on the Agent.

1. Get an out-of-band invitation. This is done using one of the two following methods: either through a QR code that resolves to a URL (most common) or from a JSON object in the form of a string.

#### Kotlin

```
  // The url commonly comes from the result of scanning a QR code.
    val invitation = OutOfBandInvitationMessage.fromUrl(url)
    // Or if you have the message in json already
    val invitation = OutOfBandInvitationMessage.fromJsonString(str)
```

#### React Native

```
  // parses the URL to a json string
    const invitation: string = agent.outOfBand.parseInvitation(invitationUrl)

```

2. Have the Agent accept the invitation. The `receiveInvitation` function has many options. To automatically make a connection with the provided invitation, supply just the invitation like the example below.

#### Kotlin

```
  // Completed connection status may or may not be done after this call
    val records = agent.outOfBand.receiveInvitation(invitation)

    //Waits for the record to be in the completed state
    agent.events.getDidExchangeEvents?.events?.first{ event ->
        if(records.didExchangeRecord.id != event.didExchangeRecord.id){
            event.didExchangeRecord.state == DidExchangeState.Completed
        }
    }
```

#### React Native

```
// Takes the json formatted string for the invitation
    const records = await agent.outOfBand.receiveInvitation(invitation)

    const removeListener = agent?.events.registerDidExchangeHandler((event) => {
              if(event.didExchangeRecord.id === records.didExchangeRecord.id && event.didExchangeRecord.isReady){
                console.log("Connection complete")
                removeListener()
              }
    })

```

3. While it is not recommended to disable auto acceptance, manually accepting a connection is an option as shown in the following code example:

#### Kotlin

```
// returns references to records needed to complete connection
val records = agent.outOfBand.receiveInvitation(invitation, autoAcceptConnection = false)

// Connection guaranteed to be completed after this call
val exchangeRecord = agent.didExchange.requestConnection(records.didExchangeRecord!!.id, records.outOfBandRecord.id)
```

#### React Native

```
const record = await agent.outOfBand.receiveInvitation(invitation, false)

const exchangeRecord = await agent.didExchange.requestConnection(records.didExchangeRecord!!.id, records.outOfBandRecord.id)
```

{% hint style="info" %}
In the above example you technically have two instances of the `didExchange` record; one in the records variable, and one in the `exchangeRecord` variable. Because records can be updated asynchronously, always use the most recently returned or fetch the record by ID in case any operations have been completed that could affect the Agent's records.
{% endhint %}

## Retrieving and Using the didExchange Records

Once a connection has been made through the `didExchange` protocol, the agent keeps a record that holds information about the established connection. A list of all connections can be retrieved using the following code example. This list can be used to create a contact list.

#### Kotlin

```
// This is a suspend function in Kotlin
val contacts: List<DidExchangeRecord> = agent.didExchange.getAll()
```

#### React Native

```
const contacts = await agent.didExchange.getAll()
```

The `didExchange` record itself has several properties that are derived when making a connection based on information provided from the connected agent. Likely properties are as follows:<br>

* `Did`
* `State`
* `Role`
* `theirDid`
* `theirLabel`
* `createdAt`
* `updatedAt`
* `Alias`
* `threadId`
* `mediatorId`
* `outOfBandId`
* `invitationDid`
* `autoAccept`

Some properties are found on all records and some are specific to `didExchange`. The `updatedAt` and `createdAt` properties are on all records along with the `id` property that is a UUID to identify the record. With `didExchangeRecords` you would likely use `theirLabel` or `alias` to create a contact card item for displaying the connection.<br>

Other properties like `role` and `state` can be used to filter the connections so that you can sort through them to find a specific connection or in rare cases find connections that failed or still need to be accepted after processing the invitation.

### Removing a Connection

If you no longer want to have a connection with another agent, you would simply have to delete the `didExchangeRecord`. If you need to reconnect, you will have to receive another invitation from the agent. Invitations can be reused in some instances (NOTE: single use invitations can only be used by one agent while multi-use can be used by multiple).

Deleting a connection is done as follows:

#### Kotlin

```
  val contacts = agent.didExchange.getAll()
  // Deleting the first contact in the list
  agent.didExchange.deleteById(contacts[0].id)
```

#### React Native

```
const contacts = await agent.didExchange.getAll()

await agent.didExchange.deleteById(contacts[0].id)
```

## Credentials

Agents can be configured to automatically or manually accept credentials. Credentials are generally offered by an external Agent upon connection or other business action. In some rare cases, the receiving Agent will request a credential.

If credentials are not auto accepted, use the credential events to determine when a credential has been offered.

The below block accepts the offer coming from an external issuing Agent. The issuer then sends a credential-issuance message that needs final confirmation before the credential is saved and the issuer is notified of completion of issuance.

#### Kotlin

```
  val credEvents = agent.events.getCredentialEvents()!! // Assuming agent initialized normally

    val offers = credEvents.events.filter {
        it.credentialExchangeRecord.state == CredentialState.OfferReceived
    }

    offers.onEach{
        // present to user
        if(userAccept)
            agent.credentials.acceptOffer(it.credentialExchangeRecord.id)
    }.collect()
```

#### React Native

```
  agent.events.registerCredentialHandler((event) => {
        if(event.credentialExchangeRecord.state === CredentialState.OfferReceived)
            // Present to user somehow
            if(userAccept)
                await agent.credentials.acceptOffer(event.credentialExchangeRecord.id)
    })
```

The following code block has the Agent confirm that it accepts the issued credential. It also notifies the issuing Agent we have accepted and stored the credential, and the transaction should be recorded on the ledger. This second confirmation is done to ensure that the credential you were offered matches what you actually received.

#### Kotlin &#x20;

```
val receivedCredentials = credEvents.events.filter{
        it.credentialExchangeRecord.state == CredentialState.CredentialReceived
    }

    receivedCredentials.onEach{
        val attributes = it.credentialExchangeRecord.attributes       

        // User reviews the attributes of the credential
        if(userAccept)
            agent.credentials.acceptCredential(it.credentialExchangeRecord.id)
        else
            agent.credentials.rejectCredential(it.credentialExchangeRecord.id)
    }.collect()
```

#### React Native

```
  agent.events.registerCredentialHandler((event) => {
        const attributes = event.credentialExchangeRecord.attributes
        // Have user review credential
        if(userAccept)
            await agent.credentials.acceptCredential(event.credentialExchangeRecord.id)
        else
            await agent.credentials.rejectCredential(event.credentialExchangeRecord.id)
    })
```

Once the credential has been accepted, it is stored in the wallet. The credentials module on the Agent has several accessor functions to get credential exchange records. The records have the previewed attributes. After being accepted, they will have a populated credential attribute that contains information about the accepted credential. The credential attribute allows a developer using the SDK to fetch more detailed information about the credential, such as the credential definition ID or schema ID. (These values are not commonly used but may need to be checked in some rare use cases.)

#### Kotlin

```
  val record = agent.credentials.findByRecordId("Some id")
```

#### React Native

```
  const record = await agent.credential.findByRecordId("Some id")
```

## Proofs

When a proof request is received, an event will be emitted. There are two options for handling proofs, attempt to auto accept and process the proof or manually select credentials. A proof request can be automatically accepted if the request does not have any attributes that need to be self attested (provided manually from the user) and there are sufficient credentials in the wallet.

A single proof request can contain multiple proofs that need to be satisfied and in turn can make manual selection complicated.

A proof event contains the proof record and the `didExchangeId` of the contact that it came from. The initial state for the proof record is `requestReceived`.

### Auto Accepting

#### Kotlin

```
  //Gets the first proof event received, blocks whatever thread it is ran on.
    val proofEvent = agent.events.getProofEvents().first()

    try {
        agent.proofs.autoAcceptProof(proofEvent.proofRecord.id, proofEvent.didExchangeId)
    } catch (e: Throwable) {
        println("An error occurred auto-accepting proof, message: ${e.message}")
    }
```

#### React Native  &#x20;

```
const removeProofHandler = agent.events.registerProofHandler(
        (event) => {
            try {
                if (event.proofRecord.state === ProofState.REQUEST_RECEIVED) {
                    await agent.proofs.autoAcceptProof(
                    event.proofRecord.id,
                    event.exchangeId);
                }
            } catch (error) {
                console.log(`An error ocurred auto-accepting proof, message ${error.message}`)
            }
        }
    )
```

{% hint style="info" %}
This only works if there are not self-attested attributes for the request and the wallet contains sufficient credentials.
{% endhint %}

### Manual Acceptance

Manual acceptance has two possible flows. One, the agent can auto select credentials, return them to you, and allow you to fill in any self-attested values. Or two, the agent returns all potential credentials that satisfy the proof. Both options will throw a `ProvenError` if the wallet does not contain sufficient credentials for the proof request.

#### Kotlin

```
  val proofEvent = agent.events.getProofEvents().first()

    // Returns a list of pairs containing first the proof and then the credential(s) selected
    val proofAttributes: SelectedCredentialsForProof = agent.proofs.autoSelectCredentialsForProof(
        proofEvent.proofRecord.id,
        proofEvent.didExchangeId,
        nonRevoked = false
    ) // nonRevoked indicates if you care if the credentials selected are revoked or not

    // Or

    // Returns a similar object but can potentially contain multiple credentials that need to be selected from
    val proofAttributes: SelectedCredentials = agent.proofs.getCredentialsForProofRequest(
        proofEvent.proofRecord.id,
        proofEvent.didExchangeId,
        nonRevoked = false
    )
```

#### React Native

```
  const removeProofHandler = agent.events.registerProofHandler(async (event) => {
        const proofAttributes = await agent.proofs.autoSelectCredentialsForProof(
            event.proofRecord.id,
            event.didExchangeId,
            false
        )

        // Or

        const proofAttributes = await agent.proofs.getCredentialsForProofRequest(
            event.proofRecord.id,
            event.didExchangeId,
            false
        )
    })

```

The return from both of these functions are a `PresentationData` object, with the auto select function having some data already filled in.

The `PresentationData` object contains four properties: `proofId`, `exchangeId`, `attributes`, and `predicates`. The last two are arrays. It also contains some helper functions:

* `getRemaining`: returns an array of the remaining `proofReferent` that do not have a selection.
* `isReady`: returns a Boolean that indicates if a valid selection has been made for all `proofReferents`.
* `autoSelect`: does an in-place auto-selection of the provided credentials and returns a Boolean indicating if it successfully made a selection for all requests referents.
* `getRequiredSelfAttested`: returns a list of all attributes required to be self-attested.

The `attributes` and `predicates` arrays contain two similar objects called `attributeReferent` and `predicateReferent` which are the two types of `proofReferents`. The objects contain three properties that all pertain to self-attesting during a proof, which is only available with an attribute and not a predicate value:&#x20;

* `canSelfAttest`: indicates if the value has to be self attested or not
* `requiredSelfAttest`: indicates if the value must be self attested
* `selfAttesdedValue`: is a mutable string that should be set to data not in a credential (ie. user input or from elsewhere in the app).

There are three object properties on the `proofReferent` objects.&#x20;

* `selection`: is the credential match that you use to satisfy the `proofReferent`. It can be set using the `selectCredential` function which takes a `credentialMatch` object or an index corresponding to a value in the `credentials` array.&#x20;
* `credentials`: is an array of all the credentials that satisfy the `proofReferent`.
* `request`: is the actual object that indicates what data is being requested from the wallet.

The differences between `attributeReferent` and `predicateReferent` are that the `predicateReferent` also has a requirement string that indicates the relationship the requested data needs to have to the requested value. Additionally predicates should not be self attested and as such should always return false self-attested values.

Self-attested values are intended to be entered in-place or as part of your written code. They are not automatically provided by the credential. If an optional self-attested attribute is supplied, it will be given priority over any supplied credential value.

The code below will go through processing the return from both functions starting with auto selection.

#### Auto selection

```
  val proofData = agent.proofs.autoSelectCredentialsForProof(
        proofEvent.proofRecord.id,
        proofEvent.didExchangeId,
        nonRevoked = false
    )

    // Check if there are any required self attested attributes
    val selfAttested = proofData.getRequiredSelfAttested()
    if(selftAttested.size != 0){
        selfAttested.forEach{ referent ->
            referent.selfAttestedValue = "Data from somewhere else"
        }
    }

    // This code provides a self attested value for all values that can be self attested but not all values have to be.

    // Supply the originally returned object that has been modified in place
    agent.proofs.acceptProofs(proofData)
```

#### React Native

```
  await const proofData = agent.proofs.autoSelectCredentialsForProof(
        event.proofRecord.id,
        event.didExchangeId,
        false
    )

    const selfAttested = proofData.getRequiredSelfAttested()

    if(selfAttested.size != 0) {
        selfAttested.forEach((referent) => {
            referent.selfAttestedValue = "Data from somewhere else"
        })
    }

    await agent.proofs.acceptProofs(proofData)
```

#### Manual Selection

Self-attested attributes function the same way for manual selection.

Manual selection requires that a credential from the return list be selected for each referent in the proof. This allows the end user to select exactly what credentials are shared but is more complex to process.

```
  val proofData = agent.proofs.getCredentialsForProofRequest(
        proofEvent.proofRecord.id,
        proofEvent.didExchangeId,
        nonRevoked = false
    )

    // Goes through all attributes and picks the first credential that matches
    proofData.attributes.forEach{ attribute =>
        attribute.selectCredential(0)
    }

    proofData.predicates.forEach{ predicate =>
        predicate.selectCredential(0)
    }

    agent.proofs.acceptProofs(proofData)
```

#### React Native

```
  await const proofData = agent.proofs.getCredentialsForProofRequest(
        proofEvent.proofRecord.id,
        proofEvent.didExchangeId,
        nonRevoked = false
    )

    proofData.attributes.forEach((attribute) => {
        attribute.selectCredential(0)
    })

    proofData.predicates.forEach((predicate) => {
        predicate.selectCredential(0)
    })

    await agent.proofs.acceptProofs(proofData)
```

<figure><img src="/files/YM9PEo5ipqfzTdcnWRgP" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.indicio.tech/developer/mobile-solutions/holdr+/holdr-integration-tutorial.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
