# Onlo Mobile SDK for Android

Install Onlo, send a real message, identify signed-in customers, and deliver reply notifications in one Android guide.

- **Audience:** Android developers adding Onlo to a native app
- **Intent:** Build with Mobile SDK
- **Version:** SDK 0.3.2
- **Active work:** 20 minutes
- **Waiting:** Package download and native build time
- **Where this happens:** Android app source code and an Activity that can open Messenger

Canonical page: https://onlo.ai/docs/developers/mobile-sdk/install/android

## Create the integration in Onlo

WebChat → Install → Mobile app → Android

## Before you start

- Android API 24 or newer
- Compile SDK 35, Java 17, and a Kotlin 2.0-compatible project
- An Onlo workspace where you can open WebChat → Install

## Quick start

Start anonymously even if your app has accounts. This proves the package, public key, network path, Messenger UI, and Inbox delivery before identity or push adds more moving parts.

1. **Create an Android Mobile SDK key.** In Onlo, open WebChat → Install → Mobile app, choose Android, and select Generate key. Copy the public SDK key. It identifies this integration and is safe in app configuration; it is not the customer identity secret.
   - **Expected result:** Onlo shows an active Android SDK key and the released package version.
2. **Install the released package.** Add the package exactly once. Wrapper apps must not install the iOS or Android core separately.
   ```kotlin
   // app/build.gradle.kts
   dependencies {
       implementation("ai.onlo:onlo-android-sdk:0.3.2")
   }
   ```
   - **Expected result:** The Android project resolves the Onlo import and builds.
3. **Initialize, start an anonymous session, and open Messenger.** Initialize once during app startup. Start the customer session before the Support control calls present. Replace the example key with the public key copied from Onlo.
   ```kotlin
   import ai.onlo.sdk.Onlo // ai.onlo:onlo-android-sdk:0.3.2
   import ai.onlo.sdk.messenger.OnloMessenger
   import androidx.lifecycle.lifecycleScope
   import kotlinx.coroutines.launch
   
   val onlo = Onlo.initialize(
       applicationContext,
       sdkKey = "onlo_public_sdk_key_from_dashboard",
   )
   
   supportButton.isEnabled = false
   lifecycleScope.launch {
       onlo.loginUnidentifiedUser()
       supportButton.isEnabled = true
   }
   
   supportButton.setOnClickListener {
       OnloMessenger.present(this, onlo)
   }
   ```
   - **Expected result:** Tap Support and the native Onlo Messenger opens with a message composer.
4. **Prove the complete message path.** On the phone, send “Where is order #5832?”. Open Onlo Inbox and reply “Your order shipped this morning.” Return to WebChat → Install → Mobile app and refresh the SDK status.
   - **Expected result:** The conversation appears in Inbox, the reply appears in Messenger, and the target shows Connected with a recent Last seen time.

### Expected result

Run the app on a device or supported emulator, tap Support, and send “Where is order #5832?”.

**Success:** Messenger opens, the message appears in Inbox, and a reply returns to the same conversation.

**If you do not see this:**

- Copy the public key again from this platform target.
- Wait for initialization and login to finish before presenting Messenger.
- Compare your host project with the maintained sample app linked below.

- [Run the Android sample app](https://github.com/onlo-ai/onlo-mobile-sdks/tree/main/examples/android): Start from maintained working code for initialization, login, Messenger, logout, and push.
- [Browse the Mobile SDK repository](https://github.com/onlo-ai/onlo-mobile-sdks): Inspect all platform packages and examples.

## Identity verification

Do this only if customers already sign in to your app. Identity keeps conversations attached to the same customer across devices and app reinstalls. Anonymous apps can skip this section.

1. **Generate the identity secret in Onlo.** Open WebChat → Install → Mobile app → Android → Identity verification and select Generate secret. Copy it directly into your backend secret manager. Never put this secret in the app, source code, build configuration, logs, or analytics.
   - **Expected result:** Only your authenticated backend can read the identity secret.
2. **Mint a short-lived customer JWT on your backend.** Authenticate the app request first. Sign HS256 with the Onlo identity secret, use onlo-messenger as the audience, use your stable customer ID as sub, and expire the token within five minutes.
   Return userJwt to the authenticated customer. Never return ONLO_MOBILE_IDENTITY_SECRET.
   ```typescript
   import {SignJWT} from 'jose';
   
   // Run on your backend after authenticating the app customer.
   const secret = new TextEncoder().encode(
     process.env.ONLO_MOBILE_IDENTITY_SECRET,
   );
   
   const userJwt = await new SignJWT({
     name: 'Alex Morgan',
     email: 'alex@example.com',
     customAttributes: {plan: 'pro'},
   })
     .setProtectedHeader({alg: 'HS256'})
     .setSubject('customer_12345')
     .setAudience('onlo-messenger')
     .setIssuedAt()
     .setExpirationTime('5m')
     .sign(secret);
   ```
   - **Expected result:** The app receives a fresh userJwt for customer_12345, not the signing secret.
3. **Identify the customer and clear Onlo on logout.** Pass the fresh JWT straight to Onlo after your app signs in. Before your app switches accounts or finishes logout, wait for Onlo logout to complete.
   ```kotlin
   fun connectSignedInCustomer(userJwt: String) = lifecycleScope.launch {
       onlo.loginIdentifiedUser(userJwt)
   }
   
   fun disconnectCustomer() = lifecycleScope.launch {
       supportButton.isEnabled = false
       onlo.logout()
   }
   ```
   - **Expected result:** Onlo Inbox shows Alex Morgan instead of an anonymous installation, and the next app account cannot inherit that conversation.

### Expected result

Sign in as the same test customer twice, mint a new JWT each time, and open Messenger.

**Success:** Both sessions resolve to customer_12345 and show the same authorized conversation history.

**If you do not see this:**

- Confirm the JWT uses HS256 and audience onlo-messenger.
- Confirm exp is later than iat but no more than five minutes later.
- Use the same stable sub for the same customer and a different sub for a different customer.

## Push notifications

Push is optional. Add it after chat works so customers receive reply alerts while the app is in the background or terminated. Onlo should not show a system alert while the customer is already reading that conversation.

Before this section, you need a Google account, a Firebase project, permission to register an Android app and generate a service-account private key, and a physical Android device or Google Play-enabled emulator. Firebase Cloud Messaging is free. You do not need a signing certificate for FCM.

1. **Create a Firebase project and register the Android app.** Sign in to Firebase Console. Select Add project if you do not have one. Open Project overview → Add app → Android. In app/build.gradle or app/build.gradle.kts, copy the applicationId, for example com.example.store, into Android package name, then select Register app.
   - [Open Firebase Console](https://console.firebase.google.com/): Create or select the project that will send Android notifications.
   - [Firebase: Add Firebase to your Android project](https://firebase.google.com/docs/android/setup): Follow Firebase’s official Android registration guide.
   - **Expected result:** Firebase shows an Android app whose package name exactly matches the installed app applicationId.
2. **Add Firebase Messaging to the app.** On the Firebase setup screen, download google-services.json and move it to app/google-services.json. Add the Google services plugin and Firebase Messaging below, then sync Gradle. The Firebase project, JSON file, and installed application ID must all match.
   Keep environment-specific google-services.json files out of public source control.
   ```kotlin
   // Root build.gradle.kts
   plugins {
       id("com.google.gms.google-services") version "4.5.0" apply false
   }
   
   // app/build.gradle.kts
   plugins {
       id("com.google.gms.google-services")
   }
   
   dependencies {
       implementation(platform("com.google.firebase:firebase-bom:34.16.0"))
       implementation("com.google.firebase:firebase-messaging")
   }
   ```
   On Android 13+, request notification permission only after a clear customer action.
   ```xml
   <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
   
   <application ...>
       <service
           android:name=".OnloFirebaseMessagingService"
           android:exported="false">
           <intent-filter>
               <action android:name="com.google.firebase.MESSAGING_EVENT" />
           </intent-filter>
       </service>
   </application>
   ```
   - **Expected result:** Gradle sync succeeds, FirebaseMessaging resolves, and the app can ask Firebase for an FCM registration token.
3. **Give Onlo permission to send through FCM.** In Firebase, open Project settings → Service accounts and generate a private key. In Onlo, open WebChat → Install → Mobile app → Android → Push notifications, enter the application ID, upload the complete service-account JSON, and select Save FCM. Upload this file only to Onlo—never bundle it in the app or commit it to Git.
   - [Firebase: Generate a service-account key](https://firebase.google.com/docs/admin/setup#initialize_the_sdk_in_non-google_environments): Use Firebase’s official server-credential instructions.
   - **Expected result:** Onlo shows FCM ready for com.example.store.
4. **Tell Onlo where this device can receive notifications.** Ask for notification permission from a clear customer action. After the Onlo customer session is ready, forward the current provider token and every later token rotation. Forward Onlo payloads when the customer taps a notification.
   ```kotlin
   import android.app.Activity
   import ai.onlo.sdk.Onlo
   import ai.onlo.sdk.messenger.OnloMessenger
   import ai.onlo.sdk.protocol.PushProvider
   import ai.onlo.sdk.push.PushPayloadOutcome
   import androidx.lifecycle.lifecycleScope
   import com.google.firebase.messaging.FirebaseMessaging
   import com.google.firebase.messaging.FirebaseMessagingService
   import kotlinx.coroutines.CoroutineScope
   import kotlinx.coroutines.Dispatchers
   import kotlinx.coroutines.SupervisorJob
   import kotlinx.coroutines.launch
   
   class OnloFirebaseMessagingService : FirebaseMessagingService() {
       private val serviceScope =
           CoroutineScope(SupervisorJob() + Dispatchers.IO)
   
       override fun onNewToken(token: String) {
           serviceScope.launch {
               Onlo.instance().registerPushToken(PushProvider.FCM, token)
           }
       }
   }
   
   // Run from the Activity after anonymous or identified login succeeds.
   FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
       lifecycleScope.launch {
           Onlo.instance().registerPushToken(PushProvider.FCM, token)
       }
   }
   
   suspend fun handleOnloNotificationTap(
       activity: Activity,
       payload: Map<String, String>,
   ) {
       when (val result = Onlo.instance().handlePushPayload(payload)) {
           is PushPayloadOutcome.NavigationIntent ->
               OnloMessenger.openConversation(activity, result.conversationId)
           else -> Unit
       }
   }
   ```
   - **Expected result:** The current device appears under registered installations in the Onlo push status.
5. **Test the provider, then test a real reply.** First use Send to selected installation. Then send a customer message, background or close the app, and reply from Inbox. Test the real reply path because the provider test and conversation delivery are separate server paths.
   - **Expected result:** The selected-device test arrives, the real Inbox reply arrives while the app is backgrounded or closed, and tapping it opens the intended conversation.

### Expected result

Background the app, reply to “Where is order #5832?” from Inbox, and tap the notification.

**Success:** The phone displays the reply notification and opens that conversation. No system notification appears while the same conversation is visible.

**If you do not see this:**

- Confirm Onlo shows the provider as ready.
- Open the app once so its current token is registered, then select that installation again.
- Confirm the provider credential and installed app use the same application ID or Bundle ID and environment.

## Error codes

Use the stable code to decide what the app should do. Show customers a plain Support-unavailable message; record only the code, SDK version, platform, time, and request ID. Never log customer JWTs, push tokens, messages, Firebase credentials, or APNs keys.

| Code | Meaning | What to do |
| --- | --- | --- |
| invalid_request | The request is invalid. | Check the arguments passed to the SDK call. Do not retry unchanged input. |
| invalid_target_key | The mobile app target key is invalid. | Copy the public SDK key again from the same platform target in Onlo. |
| sdk_not_available | The mobile SDK session service is not available. | Keep Support unavailable briefly, then retry with backoff. |
| target_disabled | The mobile app target is disabled. | Enable the Mobile SDK target in Onlo or use an active replacement key. |
| incompatible_client | This SDK or protocol version is not supported. | Upgrade the app to Mobile SDK 0.3.2. |
| proof_required | A fresh identity proof is required. | Ask your backend for a fresh customer JWT, then identify the customer again. |
| invalid_proof | The identity proof is invalid. | Check the HS256 secret, audience, customer ID, and timestamps on your backend. |
| expired_proof | The identity proof has expired. | Mint a new customer JWT. Each token can live for no more than five minutes. |
| identity_disabled | Identified access is disabled for this app. | Enable Identity verification for this Mobile SDK target in Onlo. |
| attestation_required | App attestation is required. | Complete app attestation for the registered app target, then retry. |
| invalid_attestation | App attestation could not be verified. | Check the app identifier and provider environment before retrying attestation. |
| session_expired | The mobile session has expired. | Start a fresh anonymous or identified customer session. |
| session_revoked | The mobile session has been revoked. | Do not reuse this session. Log in again only after confirming the customer may connect. |
| forbidden_principal | This customer principal cannot access the resource. | Do not open this conversation for the current customer. |
| stale_cursor | The sync cursor is no longer valid. | Discard the local cursor and let the SDK perform a full sync. |
| idempotency_conflict | The idempotency key was reused for a different request. | Create a new operation identifier instead of reusing one for different content. |
| config_unavailable | Mobile configuration is temporarily unavailable. | Keep the last safe configuration and retry with backoff. |
| media_unavailable | Image attachments are not enabled for this app. | Remove the attachment or enable image uploads for this app in Onlo. |
| rate_limited | Too many requests. | Wait and retry with backoff. Do not loop immediately. |
| dependency_unavailable | A required service is temporarily unavailable. | Show a temporary Support-unavailable state and retry with backoff. |

## Versions

These snippets target the latest public release, 0.3.2. Upgrade the package before debugging a client-version error. Onlo publishes a migration guide only when a release changes an installation step or public API.

The 0.3.2 Android AAR is 875,450 bytes (about 855 KiB). Measure your release APK or AAB because shared dependencies and shrinking change the final increase.

- Android API 24+
- Compile SDK 35
- Java 17
- Kotlin 2.0-compatible host

- [View 0.3.2 release notes](https://github.com/onlo-ai/onlo-mobile-sdks/releases/tag/0.3.2): Review changes across all four SDK families.
- [Open the Android package](https://central.sonatype.com/artifact/ai.onlo/onlo-android-sdk/0.3.2): Inspect the currently published package.
- [Open the Android example](https://github.com/onlo-ai/onlo-mobile-sdks/tree/main/examples/android): Compare your integration with the maintained example app.

## You have completed this guide when

- [ ] The app opens Onlo Messenger from its Support control
- [ ] A message sent from the phone appears in Onlo Inbox
- [ ] The Mobile SDK target shows Connected with a recent Last seen time

## Related pages

- [Onlo Mobile SDK for iOS](https://onlo.ai/docs/developers/mobile-sdk/install/ios): Install Onlo, send a real message, identify signed-in customers, and deliver reply notifications in one iOS guide.
- [Onlo Mobile SDK for React Native](https://onlo.ai/docs/developers/mobile-sdk/install/react-native): Install Onlo, send a real message, identify signed-in customers, and deliver reply notifications in one React Native guide.
- [Onlo Mobile SDK for Flutter](https://onlo.ai/docs/developers/mobile-sdk/install/flutter): Install Onlo, send a real message, identify signed-in customers, and deliver reply notifications in one Flutter guide.
