> ## Documentation Index
> Fetch the complete documentation index at: https://docs-agents.fpt.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile SDK

> Bring the Live Chat window into Android and iOS apps with the two official SDKs

The Android and iOS SDKs put the Live Chat channel's own chat window inside your app. Both are thin wrappers around a WebView, declare no dependencies, and take exactly three parameters; everything else comes from the configuration on Console.

<Info icon="mobile-screen">
  The SDKs share their configuration with the web widget. Configure the Web widget channel first, following [Live Chat channel](/en/live-chat-channel), then take the `connectionKey` from the Config tab.
</Info>

## The three initialisation parameters

| Parameter | Description | Required |
| - | - | - |
| `appOrigin` | The chat app address, path included: `https://console-agents.fpt.ai/chat-widget` | Required |
| `connectionKey` | The Website channel's connection key, for example `wgt_abc123` | Required |
| `locale` | Language: `vi`, `en`, `ja`, `id`, `zh` | Optional |

<Warning>
  `appOrigin` must keep the `/chat-widget` path. Give it only the root domain and the WebView loads whichever app sits at the root, leaving the chat window blank.
</Warning>

<Warning>
  Unlike the website, the mobile SDKs do not read **Default language** from Console. Leave `locale` empty and the chat window always shows Vietnamese. Pass the device language into `locale` so the app speaks whatever your customer is using.
</Warning>

## Minimum requirements

| Item | Android | iOS |
| - | - | - |
| Minimum version | Android 7.0 (API 24) | iOS 14 |
| Dependencies | None - the Kotlin runtime only | None |
| Mandatory declarations | `INTERNET`. The SDK declares it in the library manifest, so you do not have to add it. | `NSPhotoLibraryUsageDescription` and `NSCameraUsageDescription` in `Info.plist` - see the warning below. |

## Getting the SDK packages

We hand the SDKs over as installation packages, not through a public repository. You receive two archives, one per platform - take only the one for the platform you build on.

<CardGroup cols={2}>
  <Card title="fpt-agent-chat-1.0.0-android.zip" icon="android">
    Unzip it, then copy the `android/maven` folder inside into your project, for example into `libs/fpt-agent-chat/maven`. The `android/chat-1.0.0.aar` file beside it is the standalone build, for projects that cannot add a Maven repository.
  </Card>

  <Card title="fpt-agent-chat-1.0.0-ios.zip" icon="apple">
    Unzip it, then drag `ios/FptAgentChat.xcframework` into your target.
  </Card>
</CardGroup>

<Warning>
  On macOS, unzip with Finder or the `unzip` command, and do not re-zip the extracted folder. `.xcframework` is a bundle; re-zipping it with another tool can break the links inside it and Xcode then reports errors that are hard to read.
</Warning>

<Note>
  Each archive ships with a `README.md` that lists every step for that platform.
</Note>

### Declaring the library - Android

```kotlin theme={null}
// settings.gradle.kts - point at the Maven repository shipped in the package
dependencyResolutionManagement {
    repositories {
        maven { url = uri("$rootDir/libs/fpt-agent-chat/maven") }
        google()
        mavenCentral()
    }
}
```

```kotlin theme={null}
// build.gradle.kts of the app module
implementation("vn.fptsmartcloud.agent:chat:1.0.0")
```

### Declaring the library - iOS

Drag `ios/FptAgentChat.xcframework` into the target, then make sure it is listed under **Frameworks, Libraries, and Embedded Content** with **Embed & Sign**.

## Android

### Open the chat window full screen

```kotlin theme={null}
val config = FptChatConfig(
    appOrigin     = "https://console-agents.fpt.ai/chat-widget",
    connectionKey = "wgt_abc123",
    locale        = "vi"
)
FptChat.open(context, config)
```

### Embed the chat window in an existing screen

```kotlin theme={null}
val chat = findViewById<FptChatView>(R.id.chat)
chat.load(config)
```

### Required when you embed FptChatView yourself

`FptChatActivity` already wires the file picker up. If you place `FptChatView` on a screen of your own, you have to wire it yourself: without it the attachment button does nothing when tapped, and because the callback is left hanging, the second tap does nothing either.

```kotlin theme={null}
private var pendingFiles: ValueCallback<Array<Uri>>? = null

chat.fileChooserHandler = { intent, callback ->
    pendingFiles?.onReceiveValue(null)   // never leave the previous callback hanging
    pendingFiles = callback
    startActivityForResult(intent, REQ_FILES)
    true
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode != REQ_FILES) return
    val cb = pendingFiles ?: return
    pendingFiles = null
    cb.onReceiveValue(
        if (resultCode == RESULT_OK && data?.data != null) arrayOf(data.data!!)
        else null
    )
}
```

<Note>
  The Activity holding `FptChatView` also needs `android:windowSoftInputMode="adjustResize"` in `AndroidManifest.xml`, otherwise the keyboard covers the message box. `FptChatActivity` already carries this attribute.
</Note>

### Handle the back button

```kotlin theme={null}
override fun onBackPressed() {
    if (!chat.onBackPressed()) super.onBackPressed()
}
```

### Lifecycle

```kotlin theme={null}
override fun onPause()   { chat.pause();   super.onPause() }
override fun onResume()  { super.onResume(); chat.resume() }
override fun onDestroy() { chat.release(); super.onDestroy() }
```

<Note>
  Call all three lifecycle methods. Skipping `release()` keeps the WebView alive after the screen closes and leaks memory.
</Note>

## iOS

### Required: two keys in Info.plist

The chat window lets customers send attachments and `chat.allow_attachments` is on by default, so this is the normal path rather than a rare case. If `Info.plist` is missing `NSPhotoLibraryUsageDescription` or `NSCameraUsageDescription`, the operating system **closes your app** the moment the customer taps the attachment button - not an error message, an exit. Declare both, in wording a customer can read.

```xml theme={null}
<key>NSPhotoLibraryUsageDescription</key>
<string>The app needs access to your photo library so you can send pictures in the conversation.</string>
<key>NSCameraUsageDescription</key>
<string>The app needs access to the camera so you can take and send pictures in the conversation.</string>
```

### Open the chat window full screen

```swift theme={null}
let config = FptChatConfig(
    appOrigin: "https://console-agents.fpt.ai/chat-widget",
    connectionKey: "wgt_abc123",
    locale: "vi"
)
try FptChat.present(from: self, config: config)
```

### Embed the chat window in an existing screen

```swift theme={null}
let chat = FptChatView()
try chat.load(config)
```

### Open external links

```swift theme={null}
chat.externalLinkHandler = { url in UIApplication.shared.open(url) }
```

### Going back inside the WebView

```swift theme={null}
if !chat.goBack() { navigationController?.popViewController(animated: true) }
```

## Things to remember

<CardGroup cols={2}>
  <Card title="Configuration lives on Console" icon="sliders">
    Colours, greeting, suggested questions and the attachment policy all come from the Customize widget tab. Change them on Console and the app follows immediately - no new release needed.
  </Card>

  <Card title="Language has to be passed in" icon="language">
    This is the one difference from the web. Read the device's system language and pass it into `locale`.
  </Card>

  <Card title="One channel, several touchpoints" icon="share-nodes">
    Website and mobile app share a single `connectionKey`, so conversations and configuration are one and the same.
  </Card>

  <Card title="A new channel means a new key" icon="key">
    Delete the Website channel and recreate it and you get a new `connectionKey`. Apps in the field have to be updated with it.
  </Card>
</CardGroup>

## Pre-release checklist

* The Web widget channel has been configured and saved at least once on Console.
* `appOrigin` was taken from Console and still carries the `/chat-widget` path.
* `connectionKey` matches the channel of the agent you want.
* The device `locale` is being passed, since the SDK does not read the default language from Console.
* On iOS: `Info.plist` carries `NSPhotoLibraryUsageDescription` and `NSCameraUsageDescription` - without them the app exits when the customer taps the attachment button.
* On Android, if you embed `FptChatView` yourself: `fileChooserHandler` and `onActivityResult` are wired, and the Activity sets `windowSoftInputMode="adjustResize"`.
* Open, close and go back several times to be sure the lifecycle is handled correctly.
* Try it on a weak connection to see whether the loading state and error messages still make sense.

<Info icon="table-list">
  Attachment size limits, permitted formats and the shared error codes are in the [Technical appendix](/en/technical-appendix).
</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.