Android Integration
1. Install the SDK#
Add the dependency to your app-level build.gradle:
dependencies {
implementation("org.very:sdk:1.0.64")
} 2. Configure your app#
Add permissions to AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" /> 3. Enroll a new user#
Pass null for userId to register a new user. The SDK opens a consent screen, then guides the user through a palm scan.
import org.very.sdk.VerySDK
import org.very.sdk.VeryConfig
import org.very.sdk.VeryResult
import org.very.sdk.VeryPresentationStyle
// Check device support first
if (!VerySDK.isSupported(context)) {
Log.w("Very", "Device not supported")
return
}
val config = VeryConfig(
sdkKey = "your_sdk_key", // from Developer Portal
userId = null, // null = new enrollment
clientReferenceId = "gate-withdrawal-42", // your transaction/session ID
themeMode = "dark" // "dark" or "light"
)
VerySDK.authenticate(
context = this,
config = config,
presentationStyle = VeryPresentationStyle.FULL_SCREEN
) { result: VeryResult ->
if (result.isSuccess) {
Log.d("Very", "User ID: ${result.userId}")
Log.d("Very", "User status: ${result.userStatus}")
Log.d("Very", "Signed token: ${result.signedToken}")
} else {
Log.e("Very", "Error: ${result.errorType} — ${result.errorMessage}")
}
} 4. Verify an existing user#
Pass the user's ID from a previous enrollment to verify their identity.
val config = VeryConfig(
sdkKey = "your_sdk_key",
userId = "vu-1ed0a927-...", // existing user's ID from previous enrollment
themeMode = "dark"
)
VerySDK.authenticate(
context = this,
config = config,
presentationStyle = VeryPresentationStyle.FULL_SCREEN
) { result: VeryResult ->
if (result.isSuccess) {
Log.d("Very", "Verified user: ${result.userId}")
Log.d("Very", "Signed token: ${result.signedToken}")
} else {
Log.e("Very", "Error: ${result.errorType} — ${result.errorMessage}")
}
} 5. Verify the signed token (backend)#
When authentication succeeds, the SDK returns userId and signedToken. Send signedToken to your backend:
POST /api/verify-palm
Content-Type: application/json
{
"signedToken": "eyJhbGciOiJFZERTQSIsImtpZCI6..."
}
Follow the secure backend verification example to validate the EdDSA signature, key ID, issuer, app audience, time window, action, session and token IDs, and expected user. After all checks pass, trust sub as the user's VeryAI ID:
{
"iss": "https://api.very.org",
"sub": "vu-1ed0a927-a336-45dd-9c73-20092db9ae8d",
"aud": "your_app_id",
"act": "verify",
"sid": "sdk_session_id",
"jti": "34fa6d08-28d4-4a29-85f8-5064ea8ae32e",
"exp": 1761010175,
"iat": 1761009875
} - For enrollment: store the
sub(user ID) - use it for future verifications - For verification: confirm the
submatches the expected user codeis an SDK status string, not an OAuth authorization code
VeryConfig#
| Parameter | Type | Description |
|---|---|---|
sdkKey | String | Your SDK API key from the Developer Portal |
userId | String? | null for enrollment, user ID for verification |
clientReferenceId | String? | Your transaction or session ID (up to 255 characters), included in webhook events for the review started by this SDK call |
language | String? | BCP 47 locale code (e.g. "en", "es", "en-IN", "zh-HK") — see supported languages. Defaults to device language. |
themeMode | String | "dark" (default) or "light" |
customStrings | Map<String, String>? | Override the palm-scan status copy, keyed by VeryCustomString. See Custom strings. |
Custom strings#
Override the palm-scan status copy with customStrings, keyed by the stable VeryCustomString constants. A caller-supplied value takes priority over the SDK's built-in localized string; blank values and unknown keys fall back to the localized default. The SDK renders your value verbatim, so pass already-localized text.
import org.very.sdk.VeryCustomString
// Override the palm-scan status copy. A caller value wins over the
// built-in localized string; blank values and unknown keys fall back
// to the localized default. Pass your own already-localized text.
val config = VeryConfig(
sdkKey = "your_sdk_key",
userId = null,
customStrings = mapOf(
VeryCustomString.SHOW_YOUR_HAND to "Hold up your palm",
VeryCustomString.SHOW_YOUR_FIRST_HAND to "Hold up your first palm",
VeryCustomString.CONNECT_DOTS to "Connect the dots",
)
) | Key | Default text | Where it shows |
|---|---|---|
VeryCustomString.SHOW_YOUR_HAND | Show your hand | Default scan-page status |
VeryCustomString.SHOW_YOUR_FIRST_HAND | Show your first hand | Scan-page status during first-time enrollment |
VeryCustomString.CONNECT_DOTS | Connect the dots | "Connect the dots" gesture prompt |
VeryResult#
| Property | Type | Description |
|---|---|---|
isSuccess | Boolean | true if authentication completed successfully |
code | String | SDK status string, not an OAuth authorization code |
userId | String | The user's app-scoped VeryAI ID |
signedToken | String? | Ed25519-signed JWT for backend verification |
userStatus | String? | Consolidated status. pending is a successful submission awaiting review. |
errorType | VeryErrorType? | Typed error code (see Error Handling) |
errorMessage | String? | Human-readable error message |
Presentation styles#
FULL_SCREEN— full-screen activity (default)BOTTOM_SHEET— bottom sheet dialog
Important: Palm Verification SDK verification uses signedToken and JWKS. It does not use OAuth client_id, client_secret, or code exchange.