Credential Storage
WalletSdk.credentials exposes WalletCredentials — the storage facade over the SDK's encrypted SQLCipher database. Everything the wallet persists (wallets, credentials, DIDs, sharing logs) goes through this API.
Credentials accepted via acceptOffer are stored automatically; you mainly use this API to read and display them, and to keep wallet/DID records in sync with your backend.
The VaultCredential entity
Every stored credential is a VaultCredential:
| Field | Type | Description |
|---|---|---|
id | String | Credential identifier (referenced by getCredential, sharing logs, prompts) |
wallet | String | Owning wallet's external id |
document | String | Raw credential document (e.g. the SD-JWT compact serialization) |
disclosures | String | SD-JWT disclosures associated with the credential |
format | String | Credential format, e.g. vc+sd-jwt |
parsedDocument | Map<String, dynamic> | Decoded credential payload for display |
addedOn | DateTime | When the credential was stored |
deletedOn | DateTime? | Soft-delete timestamp, null while active |
pending | bool | Whether the credential is awaiting finalization |
VaultCredential supports fromJson / toJson / copyWith for host-side serialization.
Wallets and DIDs
Credentials are grouped under a wallet keyed by your API wallet id (walletExternalId). Create or update the local wallet record after sign-in:
await sdk.credentials.ensureWallet(
externalId: apiWalletId,
userId: userId,
name: 'Primary wallet',
);
Store the holder DID your backend provisioned:
await sdk.credentials.upsertDid(
walletExternalId: apiWalletId,
did: 'did:key:z6Mk...',
alias: 'primary', // optional
document: didDocument, // optional DID document JSON
keyId: keyId, // optional
);
final didRow = await sdk.credentials.getDidByWalletExternalId(apiWalletId);
final did = didRow?['did'] as String?;
Look up stored wallets:
final byUser = await sdk.credentials.getWalletByUserId(userId);
final byExternalId = await sdk.credentials.getWalletByExternalId(apiWalletId);
// Both return StoredWallet? (localId, externalId, userId, name, timestamps).
Listing and reading credentials
// All active credentials in a wallet.
final credentials = await sdk.listCredentials(walletExternalId: apiWalletId);
// One credential by id (null when not found).
final credential = await sdk.getCredential(credentialId);
Both are also available on sdk.credentials directly. A typical list screen:
ListView(
children: [
for (final credential in credentials)
ListTile(
title: Text(credential.parsedDocument['vct']?.toString() ?? 'Credential'),
subtitle: Text('Added ${credential.addedOn} — ${credential.format}'),
onTap: () => openDetail(credential.id),
),
],
);
Saving credentials manually
acceptOffer stores credentials for you. Use saveCredential only when you obtain a credential through your own channel:
final id = await sdk.credentials.saveCredential(
credential: vaultCredential,
walletExternalId: apiWalletId,
checkDuplicate: true, // default — rejects an identical existing credential
);
Duplicate detection helpers:
final exists = await sdk.credentials.isDuplicate(
credential: vaultCredential,
walletExternalId: apiWalletId,
);
final sameTypeAndIssuer = await sdk.credentials.getCredentialByTypeAndIssuer(
walletExternalId: apiWalletId,
credential: vaultCredential,
);
Deleting credentials
Deletion is a soft delete — the row is retained with deletedOn set and excluded from listings:
await sdk.credentials.deleteCredential(credentialId);
Sharing logs
The SDK records an audit trail of presentations. Query it to build a "sharing history" screen:
// Newest-first across all credentials; optionally filter by status.
final all = await sdk.credentials.getAllSharingLogs();
// Logs for one credential.
final logs = await sdk.credentials.getSharingLogsForCredential(credentialId);
// Single entry.
final entry = await sdk.credentials.getSharingLogById(logId);
Manage entries when your app tracks a custom sharing lifecycle:
final id = await sdk.credentials.insertSharingLog(row);
await sdk.credentials.updateSharingLogStatus(id, 'completed');
await sdk.credentials.deleteSharingLog(id);
Storage security
- The database is encrypted with SQLCipher; the key comes from your
storageEncryptionKeyProviderand never leaves the device. - The SDK database (
wallet_sdk.dbby default) is fully SDK-owned — do not open or migrate it from host code. - Losing the encryption key makes stored credentials unrecoverable. Treat key storage (Keychain / EncryptedSharedPreferences) as production-critical.