Verify Users
The MIZUHIKI Identity SDK offers a simple and convenient way to complete compliant Electronic Know Your Customer (eKYC) verification in Japan. We provide pre-built UIs you can use to guide your users through the verification process.
Make sure you have already completed the SDK installation steps from our SDK Installation Guide guide.
1. Initialize the SDK
Create an SDK instance (mock or production) by passing in your project ID obtained from the MIZUHIKI team. You can use this SDK instance to access all MIZUHIKI ID functionality.
Required: Replace YOUR_PROJECT_ID with the actual project ID provided by the MIZUHIKI team during beta access setup.
- iOS
- Android
import MizuhikiIdentityCore
import MizuhikiIdentityMock
import MizuhikiIdentity
// Define your app environment with MIZUHIKI Identity SDK client
class AppEnvironment: ObservableObject {
// Replace with your actual project ID from the MIZUHIKI team
private let mizuhikiProjectId = "YOUR_PROJECT_ID"
lazy var mizuhiki: IdentityClient = {
#if DEBUG
// Use mock environment for development and testing
return MizuhikiIdentityMock.makeClient(projectId: mizuhikiProjectId)
#else
// Use production environment for release builds
return MizuhikiIdentity.makeClient(projectId: mizuhikiProjectId)
#endif
}()
// Shared instance for app-wide usage
static let shared = AppEnvironment()
}
// Usage in SwiftUI
struct ContentView: View {
@StateObject private var appEnv = AppEnvironment.shared
var body: some View {
VStack {
Text("MIZUHIKI Identity SDK Ready")
// Use appEnv.mizuhiki for all MIZUHIKI Identity SDK operations
}
.environmentObject(appEnv)
}
}
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import io.mizuhiki.identity.adapter.mock.MizuhikiMockFactory
import io.mizuhiki.identity.adapter.prod.MizuhikiProdFactory
import io.mizuhiki.identity.core.IdentityClient
// Helper class to manage MIZUHIKI client lifecycle
class MizuhikiManager private constructor() {
companion object {
// Replace with your actual project ID from the MIZUHIKI team
private const val PROJECT_ID = "YOUR_PROJECT_ID"
fun createClient(context: Context, useMock: Boolean = BuildConfig.DEBUG): IdentityClient {
return if (useMock) {
// Use mock environment for development and testing
MizuhikiMockFactory.makeClient(context, PROJECT_ID)
} else {
// Use production environment for release builds
MizuhikiProdFactory.makeClient(context, PROJECT_ID)
}
}
}
}
// Usage in Composable
@Composable
fun HomeScreen() {
val context = LocalContext.current
val mizuhikiIdentityClient = remember(context) {
MizuhikiManager.createClient(context)
}
// Use mizuhikiIdentityClient for all MIZUHIKI Identity SDK operations
Text("MIZUHIKI Identity SDK Ready")
}
Important Notes:
- 🔑 Project ID: Must match the ID provided in your MIZUHIKI Identity SDK credentials
- 🏗️ Environment: Mock for development, Production for release
- 💾 Instance Management: Create one client instance per app session
- 🔄 Lifecycle: Client handles connection management automatically
2. Use the built-in MIZUHIKI UIs to complete KYC
The MIZUHIKI Identity SDK comes with a pre-built UI flow to guide users through the KYC process.
The KYC process includes the following screens:
- Introduction screen with explanation of the process
- Password entry screen
- MyNumber card scanning instructions
- NFC scanning in progress
- Success screen
If you would like to use your own UI with the MIZUHIKI API, let us know
User information format
After successful KYC, the SDK returns the user's personal information (name, date of birth, address, gender) encrypted as a JWE object.
Send this JWE to your backend for decryption and processing. Do not decrypt user information (PII) in the mobile app.
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
address | String | Yes | Residential address | 徳島県板野町犬伏9番8 |
dateOfBirth | String | Yes | Date of birth in western calendar | 2000-12-25 |
fullName | String | Yes | Full name | 山中 逸穂 |
gender | Enum (female | male | other) | Yes | Gender | male |
- iOS
- Android
import MizuhikiIdentityCore
import MizuhikiIdentityUI
struct ContentView: View {
@Environment(\.appEnv) private var appEnv
var body: some View {
VStack {
Button(action: kyc) {
Text("KYC")
.padding(8)
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
}
}
}
@State private var signedUpUser: MizuhikiIdentityCore.User?
private func onSignUp() {
MizuhikiIdentityUIBridge.present(config: .init(client: appEnv.mizuhiki)) { event in
switch event {
case .didStart:
break
case .didFinish(let result):
self.signedUpUser = result.user
// submit the encrypted JWE to your backend for decryption and storage, see details in the next step
submitEncryptedPii(jwe: result.kycDetails)
case .didCancel:
break
}
}
}
}
package com.example.dapp
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import android.content.Context
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.tooling.preview.Preview
import com.example.dapp.ui.theme.DAppTheme
import io.mizuhiki.identity.adapter.mock.MizuhikiMockFactory
import io.mizuhiki.identity.adapter.prod.MizuhikiFactory
import io.mizuhiki.identity.core.IdentityClient
import io.mizuhiki.identity.ui.MizuhikiUIBridge
import io.mizuhiki.identity.ui.UIConfig
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DAppTheme {
HomeScreen(name = "Android")
}
}
}
}
// Helper class to manage MIZUHIKI Identity SDK client lifecycle
class MizuhikiManager private constructor() {
companion object {
// Replace with your actual project ID from the MIZUHIKI team
private const val PROJECT_ID = "YOUR_PROJECT_ID"
fun makeClient(context: Context, useMock: Boolean = (BuildConfig.FLAVOR == "mock")): IdentityClient {
return if (useMock) {
// Use mock environment for development and testing
MizuhikiMockFactory.makeClient(context, PROJECT_ID)
} else {
// Use production environment for release builds
MizuhikiFactory.makeClient(context, PROJECT_ID)
}
}
}
}
@Composable
fun HomeScreen(name: String, modifier: Modifier = Modifier) {
val context = LocalContext.current
val client = remember(context) {
MizuhikiManager.makeClient(context)
}
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Column(
modifier = modifier
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Hello $name!",
modifier = modifier
)
Button(onClick = {
MizuhikiUIBridge.present(
context,
config = UIConfig(client = client),
onEvent = { event ->
Log.i("mizuhiki-ui", event.toString())
when (event) {
UIEvent.DidStart, UIEvent.DidCancel -> Unit
// handle successful KYC and submit the encrypted JWE to your backend for decryption and storage, see details in the next step
is UIEvent.DidFinish -> onKycSuccess(event.result.user, event.result.kycDetails)
}
})
}) {
Text("Start KYC")
}
}
}
}
3. Forward encrypted user information (JWE) to your backend
When KYC completes, your app receives encrypted user personal information in JWE format.
Pass it directly to your backend API over HTTPS.
- iOS
- Android
// Example payload you send to your backend after successful KYC
struct KycResultPayload: Encodable {
let encryptedPiiJwe: String
}
func submitEncryptedPii(_ jwe: String) async throws {
let payload = KycResultPayload(encryptedPiiJwe: jwe)
// POST payload to your backend over HTTPS
}
// Example payload you send to your backend after successful KYC
@Serializable
data class KycResultPayload(
val encryptedPiiJwe: String
)
suspend fun submitEncryptedPii(jwe: String) {
val payload = KycResultPayload(encryptedPiiJwe = jwe)
// POST payload to your backend over HTTPS
}
4. Decrypt JWE on your backend
Use your private key generated during setup to decrypt the JWE and parse the user information payload.
import { compactDecrypt, CompactJWEHeaderParameters } from "jose";
import { createPrivateKey } from "node:crypto";
type KycPiiGender = "female" | "male" | "other";
interface KycPii {
// in ""徳島県板野町犬伏9番8" format
address: string;
// in "2000-12-25" basic ISO format
dateOfBirth: string;
// in "山中 逸穂" format
fullName: string;
gender: KycPiiGender;
}
interface DecryptedKycPii {
pii: KycPii;
protectedHeader: CompactJWEHeaderParameters;
}
export async function decryptMizuhikiPii(jwe: string): Promise<DecryptedKycPii> {
const privateKeyPem = process.env.MIZUHIKI_PII_PRIVATE_KEY_PEM;
if (!privateKeyPem) {
throw new Error("Missing MIZUHIKI_PII_PRIVATE_KEY_PEM");
}
const privateKey = createPrivateKey(privateKeyPem);
const { plaintext, protectedHeader } = await compactDecrypt(jwe, privateKey);
const json = new TextDecoder().decode(plaintext);
const pii = JSON.parse(json) as KycPii;
return { pii, protectedHeader };
}
Backend handling requirements:
- Accept JWE only over TLS (HTTPS).
- Decrypt only on trusted server infrastructure.
- Avoid logging raw JWE or decrypted PII.
- Apply least-privilege access controls to decrypted data.





Identity verification flow with the MIZUHIKI ID App
Next Steps
After completing KYC, you are ready to mint a MIZUHIKI Verified SBT to the user's address
Continue to: SBT Minting Guide →
Troubleshooting
Common Issues and Solutions
NFC-Related Issues
Issue: NFC Permission Denied
- Symptoms: App crashes when attempting to scan MyNumber card
- iOS Solution:
- Verify NFC capability is added in Xcode project settings
- Check
Info.plistcontainsNFCReaderUsageDescription - Ensure device supports NFC (iPhone 7+ for NFC reading)
- Android Solution:
- Add
<uses-permission android:name="android.permission.NFC" />toAndroidManifest.xml - Verify device has NFC hardware and it's enabled in settings
- Add
Issue: Card Not Detected
- Symptoms: NFC scanning doesn't detect the card
- Solutions:
- Remove card from wallet/case - avoid interference
- Hold card steady against device's NFC area for at least 5 seconds
- Try different positioning - NFC antenna location varies by device
- Ensure card is not damaged or demagnetized
Authentication Issues
Issue: Invalid Project ID
- Symptoms: Authentication errors during SDK usage (
"message": "permission denied: invalid x-project-id") - Solution: Verify project ID matches exactly the one provided by the MIZUHIKI team (case-sensitive)
Issue: Mock Environment Card Rejection
- Symptoms: Valid FeliCa card rejected in mock environment
- Solution:
- Confirm using mock SDK version (
mizuhiki-adapter-mock) - Use password
ABC123for all mock cards - Verify card is FeliCa or ISO/IEC 14443-4 Type-A compatible
- Confirm using mock SDK version (
Issue: Production Password Lock
- Symptoms: MyNumber card becomes unusable after failed attempts
- Solution:
- Visit convenience store (7-Eleven, FamilyMart) with card and ID
- Use card reader terminal to unlock with PIN reset procedure
- Alternative: Visit city hall for manual unlock process
Integration Issues
Issue: Build Errors with Package Registry
- Symptoms: Cannot resolve MIZUHIKI Identity SDK dependencies
- iOS Solution:
# Re-authenticate with package registryswift package-registry login https://swift.cloudsmith.io/mizuhiki/compliance/ --token YOUR_TOKEN --no-confirm# Clear Xcode derived datarm -rf ~/Library/Developer/Xcode/DerivedData
- Android Solution:
# Verify environment variable is setecho $MIZUHIKI_TOKEN# Clean and rebuild project./gradlew clean./gradlew build
Issue: UI Bridge Not Responding
- Symptoms: KYC UI doesn't appear or callbacks not triggered
- iOS Solution:
- Ensure UI calls are on main thread
- Verify
MizuhikiUIframework is imported - Check if client instance is properly initialized
- Android Solution:
- Verify context is valid Activity context
- Check if UI permissions are granted
- Ensure client is created before UI presentation
Getting Help
If you encounter issues not covered here:
- Check SDK Version: Ensure you're using the latest SDK version
- Contact Support: Email [email protected] with:
- SDK version and platform (iOS/Android)
- Environment (mock/production)
- Complete error messages and stack traces
- Minimal reproduction code example