---
title: Kindly Web View SDK
slug: sdk/web-view-sdk-skill
description: Kindly web view chat SDK setup for iOS, Android, Flutter and React Native apps. Use when the user asks to add Kindly's web chat or the Kindly web view SDK to an app, or mentions KindlyWebViewSDK, KindlyWebView, sdk-chat-webview, ai.kindly:sdk-webview…
createdAt: 2026-11-04T11:39:37.120Z
---

Integrate the Kindly web view SDK into an iOS, Android, Flutter or React Native app. The SDK shows Kindly's web chat widget in a native web view and connects it to the app through a bridge that validates every message: the app starts the chat, shows and hides it, sends messages, triggers dialogues, passes context, authenticates its users and listens to chat events. The chat's look, texts and behaviour come from the bot's settings in the Kindly platform

This guide matches version 1.0.1 of the four packages, which share one version. The documentation at [https://docs.kindly.ai/sdk/web-view-sdk](https://docs.kindly.ai/sdk/web-view-sdk) is the reference when something here differs

## Invoke this skill when

- The user asks to add Kindly's chat, the Kindly web chat or the Kindly web view SDK to an iOS, Android, Flutter or React Native app
- The user mentions `KindlyWebViewSDK`, `KindlyWebView`, `sdk-chat-webview`, `ai.kindly:sdk-webview`, `kindly_webview` or `@kindly-ai/react-native-webview`
- The app uses the native Kindly SDK (`KindlySDK`, `ai.kindly:sdk`, the `kindly` pub package or `@kindly-ai/react-native`) and the user wants the web chat instead of it or beside it

## Before you start

The SDK needs two values from the Kindly platform: the bot key, and the slug of a market configured for the bot (Settings, General, Details & markets). The market is required and there is no fallback to another one. When the project does not hold them, ask the user for both. Never invent either value

***

## Phase 1: Detect

Find out which kind of app this is and what is already there. Run the block for the platform you find:

```bash
# Which kind of app
ls Package.swift *.xcodeproj *.xcworkspace build.gradle build.gradle.kts pubspec.yaml package.json 2>/dev/null

# Native iOS: Kindly already added, deployment target, entry point, usage descriptions
grep -rlE 'sdk-chat-webview|sdk-chat-ios' --include='*.pbxproj' --include='Package.swift' --include='Package.resolved' . 2>/dev/null
grep -rhoE 'IPHONEOS_DEPLOYMENT_TARGET = [0-9.]+' --include='*.pbxproj' . 2>/dev/null | sort -u
grep -rlE '@main|UIApplicationMain' --include='*.swift' . 2>/dev/null | head -5
grep -rlE 'NSCameraUsageDescription|NSPhotoLibraryAddUsageDescription|WKAppBoundDomains' --include='*.plist' . 2>/dev/null

# Native Android: Kindly already added, SDK levels, Application class, camera permission
grep -E 'ai\.kindly' build.gradle* app/build.gradle* gradle/libs.versions.toml 2>/dev/null
grep -E 'minSdk|compileSdk' app/build.gradle* 2>/dev/null
grep -rlE ': Application\(\)' --include='*.kt' app/src/main 2>/dev/null | head -3
grep -E 'android:name=|android.permission.CAMERA' app/src/main/AndroidManifest.xml 2>/dev/null

# Flutter
grep -nE '^\s+(kindly|kindly_webview):' pubspec.yaml
grep -nE "platform :ios" ios/Podfile 2>/dev/null
grep -nE 'repositoriesMode' android/settings.gradle* 2>/dev/null

# React Native
grep -nE '"react-native"|"@kindly-ai/react-native(-webview)?"' package.json
grep -nE "platform :ios" ios/Podfile 2>/dev/null
grep -nE 'repositoriesMode' android/settings.gradle* 2>/dev/null
```

What to note:

| Question                                            | Why it matters                                                         |
| --------------------------------------------------- | ---------------------------------------------------------------------- |
| Native iOS, native Android, Flutter or React Native | Picks the package and the steps below                                  |
| The web view SDK already in the project             | Skip to the first step that is missing                                 |
| The native Kindly SDK in the project                | Both can stay, see Next to the native SDK below                        |
| iOS deployment target                               | 15.4 or later on every platform                                        |
| Android `minSdk`                                    | 21 or later for a native app, 24 or later for Flutter and React Native |
| React Native version                                | 0.82 or later, with the New Architecture                               |
| Where the app starts                                | `start` goes there                                                     |
| Usage descriptions in `Info.plist`                  | The chat needs all four, see Phase 3                                   |

***

## Phase 2: Install

### Native iOS

Swift Package Manager only. The SDK is a signed binary, `KindlyWebView.xcframework`, and there is no CocoaPods pod for native iOS apps. In a `Package.swift`:

```swift
dependencies: [
    .package(url: "https://github.com/kindly-ai/sdk-chat-webview.git", from: "1.0.1"),
],
targets: [
    .target(name: "YourApp", dependencies: [
        .product(name: "KindlyWebView", package: "sdk-chat-webview"),
    ]),
]
```

In an Xcode project, add the package in Xcode: File > Add Package Dependencies, the URL `https://github.com/kindly-ai/sdk-chat-webview`, then the `KindlyWebView` product on the app target. Editing `project.pbxproj` by hand is error-prone, so when you cannot run Xcode, ask the user to do this step. A beta, `X.Y.Z-beta.N`, needs an exact pin such as `exact: "X.Y.Z-beta.N"`, because `from:` never resolves a prerelease

### Native Android

The SDK is on Maven Central. Kotlin DSL:

```kotlin
dependencies {
    implementation("ai.kindly:sdk-webview:1.0.1")
}
```

Groovy:

```groovy
dependencies {
    implementation 'ai.kindly:sdk-webview:1.0.1'
}
```

The project's repositories need `google()` and `mavenCentral()`. Pin an exact version, because a dynamic one such as `1.+` can resolve to a beta. Requirements: `compileSdk` 35 or later, and Kotlin 1.9.20 or later when the app is written in Kotlin. The SDK brings `kotlinx-coroutines-core` and depends on AndroidX WebKit, OkHttp and Sentry's JVM core `io.sentry:sentry` 8

### Flutter

```yaml
dependencies:
  kindly_webview: ^1.0.1
```

Then run `flutter pub get`. Requirements: Flutter 3.27 or later, Dart 3.6 or later, Android `compileSdk` 36 or later. The native SDK ships inside the package, so the app adds nothing else

### React Native

```bash
yarn add @kindly-ai/react-native-webview@1.0.1
cd ios && pod install
```

With npm: `npm install @kindly-ai/react-native-webview@1.0.1`. Autolinking does the rest, and the native SDK ships inside the package. A beta is published under the npm dist-tag `beta`, so a plain install never picks one

***

## Phase 3: App setup

### Usage descriptions on iOS

Every iOS app needs these four keys in its `Info.plist` (`ios/Runner/Info.plist` in Flutter). The chat lets people attach photos and videos and save images, and iOS ends an app that reaches one of these features without its usage description. Word the strings for the app:

```xml
<key>NSCameraUsageDescription</key>
<string>Take a photo or video to send in the chat</string>
<key>NSMicrophoneUsageDescription</key>
<string>Record a video with sound to send in the chat</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose photos and videos to send in the chat</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Save images from the chat to your photo library</string>
```

If the app declares `WKAppBoundDomains`, add `chat.kindlycdn.com` to it, or the chat cannot run. No `LSApplicationQueriesSchemes` entries are needed

### Deployment target

- Native iOS: 15.4 or later on the app target
- Flutter: 15.4 or later on the Runner target, and `platform :ios, '15.4'` in `ios/Podfile` with CocoaPods
- React Native: `platform :ios, '15.4'` or later in `ios/Podfile`, before `pod install`

An app built with the Xcode 27 SDK must adopt the UIScene lifecycle, or iOS 27 stops it at launch. That is Apple's rule for every app, and the SDK works under scenes. In Flutter, declare a scene configuration with `FlutterSceneDelegate` as its delegate class, as Flutter's UIScene migration guide shows

### Android

Nothing to add. The SDK's manifest brings the `INTERNET` permission, its chat activity, a `FileProvider` with the authority `${applicationId}.kindly.webview.fileprovider` and a `<queries>` entry for the camera, and its R8 rules ship inside the package

- Flutter: the plugin resolves `ai.kindly:sdk-webview` only from the Maven repository bundled in the package. Do not declare another version of that artifact, and keep the default `repositoriesMode` in `android/settings.gradle`, because `PREFER_SETTINGS` and `FAIL_ON_PROJECT_REPOS` ignore or refuse that repository
- React Native: the package registers its bundled copy of `ai.kindly:sdk-webview` as the only source for that artifact, so do not declare another version. If `android/settings.gradle` sets `repositoriesMode` to `PREFER_SETTINGS`, add `node_modules/@kindly-ai/react-native-webview/android/maven-repo` to the settings repositories

### The bot

The chat page runs on `https://chat.kindlycdn.com`, and the SDK sends the app's iOS bundle id or Android package name to the Kindly chat backend. A bot that only allows listed domains must list them, as for the native Kindly SDK. Tell the user to check this in the Kindly platform; never change a bot's settings yourself

***

## Phase 4: Start the SDK

Call `start` once when the app starts. It does not load the chat yet: the chat page is created the first time the chat is shown. It does start the SDK's usage metrics and error reports, so when the app asks for the user's consent before collecting data, call `start` after the user gives it. `languageCode` defaults to `en`

### Native iOS, UIKit

```swift
import KindlyWebView
import UIKit

@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        KindlyWebViewSDK.start(botKey: "YOUR_BOT_KEY", languageCode: "en", market: "YOUR_MARKET")
        return true
    }
}
```

### Native iOS, SwiftUI

```swift
import KindlyWebView
import SwiftUI

@main
struct MyApp: App {
    init() {
        KindlyWebViewSDK.start(botKey: "YOUR_BOT_KEY", languageCode: "en", market: "YOUR_MARKET")
    }

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}
```

Every member of `KindlyWebViewSDK` is static and can be called from any thread

### Native Android

```kotlin
import ai.kindly.webview.KindlyWebViewSDK
import android.app.Application

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        KindlyWebViewSDK.start(
            application = this,
            botKey = "YOUR_BOT_KEY",
            languageCode = "en",
            market = "YOUR_MARKET",
        )
    }
}
```

Register the class with `android:name=".App"` on the `<application>` element of `AndroidManifest.xml` when the app has no `Application` class yet. Every function can be called from any thread

### Flutter

```dart
import 'package:flutter/widgets.dart';
import 'package:kindly_webview/kindly_webview.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await KindlyWebViewSDK.start(botKey: 'YOUR_BOT_KEY', market: 'YOUR_MARKET', languageCode: 'en');
  runApp(const MyApp());
}
```

### React Native

In `index.js`, so every JavaScript reload calls it again:

```typescript
import { AppRegistry } from 'react-native';
import { KindlyWebViewSDK } from '@kindly-ai/react-native-webview';
import App from './App';
import { name as appName } from './app.json';

KindlyWebViewSDK.start({ botKey: 'YOUR_BOT_KEY', market: 'YOUR_MARKET', languageCode: 'en' }).catch((error) => {
  console.warn('Kindly start failed', error);
});

AppRegistry.registerComponent(appName, () => App);
```

### Argument rules

The bot key, market and language code are trimmed. A bot key is 8 to 64 letters, digits and hyphens, a market 1 to 64 letters, digits, `_` and `-`, and a language code looks like `en`, `nb` or `pt-BR`. iOS and Android refuse anything else with an error in the log and do nothing, Flutter throws an `ArgumentError` and React Native rejects with `INVALID_ARGUMENT`

Calling `start` again with the same bot key and market keeps the live chat and replaces the language, for the next chat page, and the auth provider. A call without an auth provider removes it. A different bot key or market closes the chat and starts over

***

## Phase 5: Show the chat

From a button or wherever the app offers help:

```swift
KindlyWebViewSDK.displayChat()                                   // iOS
```

```kotlin
KindlyWebViewSDK.launchChat(activity)                            // Android, from an Activity
```

```dart
await KindlyWebViewSDK.displayChat();                            // Flutter
```

```typescript
await KindlyWebViewSDK.displayChat();                            // React Native
```

On iOS the chat opens as a full-height sheet over the top view controller, and on Android on its own full-screen screen. People close it with the chat's minimise button, by swiping the sheet down on iOS or with the back button on Android, and the conversation stays. The first display loads the widget, later ones reopen the same conversation at once. Pass `triggerDialogueId` to start a dialogue as the chat opens, or `languageCode` to show the chat in another language. On Android pass an `Activity` when you can; with another context the chat starts in a new task. Before `start`, iOS and Android log an error and do nothing, and Flutter and React Native fail with `NOT_STARTED`

***

## Common tasks

### Close, end or kill

| Call                             | Chat screen | Conversation                             | SDK                            |
| -------------------------------- | ----------- | ---------------------------------------- | ------------------------------ |
| `closeChat()`, Android `close()` | Dismissed   | Kept, the page stays loaded              | Started                        |
| `endChat()`                      | Dismissed   | Ended, the next display starts a new one | Started                        |
| `kill()`                         | Dismissed   | Ended                                    | Stopped until the next `start` |

```swift
KindlyWebViewSDK.closeChat()
await KindlyWebViewSDK.endChat()
await KindlyWebViewSDK.kill()
```

```kotlin
KindlyWebViewSDK.close()
lifecycleScope.launch { KindlyWebViewSDK.endChat().await() }
lifecycleScope.launch { KindlyWebViewSDK.kill().await() }
```

Flutter and React Native: `await KindlyWebViewSDK.closeChat()`, `await KindlyWebViewSDK.endChat()`, `await KindlyWebViewSDK.kill()`

The conversation is stored in the app's web view storage for `chat.kindlycdn.com`, per bot and market, and survives app restarts. `endChat` and `kill` delete it, so call one of them when the user signs out of the app. `kill` keeps the link delegate and forgets the auth provider

### Send a message as the user

```swift
try await KindlyWebViewSDK.sendMessage("I need help with my order", newContext: ["orderId": "1234"])
```

```kotlin
KindlyWebViewSDK.sendMessage("I need help with my order", mapOf("orderId" to "1234")).await()
```

```dart
await KindlyWebViewSDK.sendMessage('I need help with my order', newContext: {'orderId': '1234'});
```

```typescript
await KindlyWebViewSDK.sendMessage('I need help with my order', { newContext: { orderId: '1234' } });
```

It needs a chat page, so show the chat at least once first; it may be hidden since, and then the message goes without showing the chat. Otherwise it fails with the chat-not-ready error. The text is trimmed and can be up to 10,000 characters, and `newContext` goes with this message only

### Trigger a dialogue

```swift
KindlyWebViewSDK.triggerDialogue(id: "DIALOGUE_ID")
```

```kotlin
KindlyWebViewSDK.triggerDialogue("DIALOGUE_ID")
```

```dart
await KindlyWebViewSDK.triggerDialogue('DIALOGUE_ID');
```

```typescript
await KindlyWebViewSDK.triggerDialogue('DIALOGUE_ID');
```

It also needs a chat page, and runs without opening the chat while it is hidden. Without a page it does nothing, and the SDK logs an error on iOS and a warning on Android. A dialogue id is 1 to 128 letters, digits, `_` and `-`

### Context

```swift
KindlyWebViewSDK.setNewContext(["plan": "gold"])
KindlyWebViewSDK.clearNewContext()
```

```kotlin
KindlyWebViewSDK.setNewContext(mapOf("plan" to "gold"))
KindlyWebViewSDK.clearNewContext()
```

```dart
await KindlyWebViewSDK.setNewContext({'plan': 'gold'});
await KindlyWebViewSDK.clearNewContext();
```

```typescript
KindlyWebViewSDK.setNewContext({ plan: 'gold' });
KindlyWebViewSDK.clearNewContext();
```

Context goes with the next message, button click or triggered dialogue, once. The chat merges it with context it already holds, so two calls before the next message send both maps. A map holds up to 100 string entries, keys of 1 to 256 characters other than `__proto__`, `constructor` and `prototype`, and values of up to 4,096 characters

### Authentication

Pass a provider to `start`. It gets the chat id and returns a JWT or opaque bearer token for the user signed in to the app:

```swift
KindlyWebViewSDK.start(botKey: "YOUR_BOT_KEY", market: "YOUR_MARKET", authTokenProvider: { chatId in
    try await MyBackend.kindlyToken(chatId: chatId)
})
```

```kotlin
KindlyWebViewSDK.start(
    application = this,
    botKey = "YOUR_BOT_KEY",
    market = "YOUR_MARKET",
    authTokenProvider = KindlyAuthTokenProvider { request -> myBackend.kindlyToken(request.chatId) },
)
```

```dart
await KindlyWebViewSDK.start(
  botKey: 'YOUR_BOT_KEY',
  market: 'YOUR_MARKET',
  authTokenCallback: (String chatId) => myBackend.kindlyToken(chatId),
);
```

```typescript
await KindlyWebViewSDK.start({
  botKey: 'YOUR_BOT_KEY',
  market: 'YOUR_MARKET',
  authTokenCallback: async (chatId) => fetchKindlyToken(chatId),
});
```

`MyBackend`, `myBackend` and `fetchKindlyToken` stand for the app's own call to its backend; tokens come from the backend and a signing key never goes in the app. Call `authenticate()` after the user signs in and `deauthenticate()` after they sign out. Credentials providers, the timeouts and the token rules are in [AUTH.md](https://docs.kindly.ai/sdk/web-view-sdk-skill-auth.md)

### Links

No link opens inside the chat. `http`, `https`, `mailto`, `tel` and `sms` links open outside the app, and other schemes are blocked. To handle a link in the app, set a delegate and return `false` for it:

```swift
final class ChatLinks: KindlyWebViewClientDelegate {
    func shouldHandleLink(url: URL) async -> Bool {
        url.host != "myapp.example"
    }
}
let chatLinks = ChatLinks()          // keep a reference, the SDK holds it weakly
KindlyWebViewSDK.delegate = chatLinks
```

```kotlin
KindlyWebViewSDK.`interface` = object : KindlyWebViewInteraction {
    override suspend fun shouldHandleLink(url: String): Boolean = !url.startsWith("https://myapp.example/")
}
```

```dart
await KindlyWebViewSDK.setDelegate(shouldHandleLink: (String url) async => !url.startsWith('https://myapp.example/'));
```

```typescript
KindlyWebViewSDK.setDelegate({ shouldHandleLink: async (url) => !url.startsWith('https://myapp.example/') });
```

These return `false`, so the app handles the link, for `myapp.example` and `true` for every other link. Open the app's own screen where the delegate returns `false`. Timeouts, limits and patterns are in [LINKS.md](https://docs.kindly.ai/sdk/web-view-sdk-skill-links.md)

### Events and state

```swift
import Combine

var subscriptions = Set<AnyCancellable>()
KindlyWebViewSDK.events
    .sink { event in print(event.rawType) }
    .store(in: &subscriptions)
KindlyWebViewSDK.state
    .sink { state in print(state.connectionState) }
    .store(in: &subscriptions)
```

```kotlin
lifecycleScope.launch { KindlyWebViewSDK.events.collect { event -> Log.d("Chat", event.rawType) } }
lifecycleScope.launch { KindlyWebViewSDK.state.collect { state -> Log.d("Chat", state.connectionState) } }
```

```dart
final events = KindlyWebViewSDK.events.listen((KindlyWebViewEvent event) => debugPrint(event.rawType));
final state = KindlyWebViewSDK.state.listen((KindlyWebViewState state) => debugPrint(state.rawConnectionState));
```

```typescript
const events = KindlyWebViewSDK.addEventListener((event) => console.log(event.rawType));
const state = KindlyWebViewSDK.addStateListener((s) => console.log(s.connectionState));
```

Events are not replayed, and a new state listener gets the current state first. Message events carry chat content, so keep them out of logs. Event types, payloads and their limits are in [EVENTS.md](https://docs.kindly.ai/sdk/web-view-sdk-skill-events.md)

### Logging and crash reporting

| Platform     | Debug logs, off by default                       | Crash reporting to Kindly, on by default          |
| ------------ | ------------------------------------------------ | ------------------------------------------------- |
| iOS, Android | `KindlyWebViewSDK.verboseLogging = true`         | `KindlyWebViewSDK.enableCrashReporting = false`   |
| Flutter      | `await KindlyWebViewSDK.setVerboseLogging(true)` | `await KindlyWebViewSDK.setCrashReporting(false)` |
| React Native | `KindlyWebViewSDK.setVerboseLogging(true)`       | `KindlyWebViewSDK.setCrashReporting(false)`       |

Set both before `start`. Logs go to the iOS unified log under the subsystem `ai.kindly.webview` and to Logcat under the tag `KindlyWebView`, and never contain chat content, tokens or URL paths

### Privacy declarations

The app's App Store privacy details and Google Play Data safety form must cover what the SDK sends: usage metrics with a random device ID, error reports and the conversation. [Privacy and Data](https://docs.kindly.ai/sdk/web-view-sdk-privacy) maps each to the stores' data types and covers consent and bots that answer with third-party generative AI. The declarations are the user's to make, so point them to that page rather than deciding for them

### Checking support

`KindlyWebViewSDK.isSupported` on iOS, `KindlyWebViewSDK.isSupported(context)` on Android, and `await KindlyWebViewSDK.isSupported()` in Flutter and React Native are `false` when the device's web view cannot run the chat (on Android a WebView older than version 85) or, on iOS, when the app declares `WKAppBoundDomains` without `chat.kindlycdn.com`. Showing the chat then opens a notice instead. Use it to hide the way into the chat

### Next to the native Kindly SDK

Both SDKs can be in one app, with separate state, storage and conversations. On iOS, qualify the type names both modules have, such as `KindlyWebView.KindlyEvent`. On Android, import one of them under another name, such as `import ai.kindly.webview.models.KindlyEvent as KindlyWebViewEvent`. Every Flutter and React Native type of the web view packages starts with `KindlyWebView`

***

## Core public surface

### iOS, `KindlyWebViewSDK`

| API                                                            | Purpose                                         |
| -------------------------------------------------------------- | ----------------------------------------------- |
| `start(botKey:languageCode:market:authTokenProvider:)`         | Start the SDK, optionally with a token provider |
| `start(botKey:languageCode:market:authCredentialsProvider:)`   | Start the SDK with a credentials provider       |
| `displayChat(languageCode:triggerDialogueId:)`                 | Show the chat                                   |
| `closeChat()`                                                  | Hide the chat, keep the conversation            |
| `endChat() async`                                              | End the conversation                            |
| `kill() async`                                                 | End the conversation and stop the SDK           |
| `sendMessage(_:newContext:) async throws`                      | Send a message as the user                      |
| `triggerDialogue(id:)`                                         | Start a dialogue                                |
| `setNewContext(_:)`, `clearNewContext()`                       | Context for the next message                    |
| `authenticate() async throws`, `deauthenticate() async throws` | Sign the chat in and out                        |
| `delegate: KindlyWebViewClientDelegate?`                       | Link interception, held weakly                  |
| `events: AnyPublisher<KindlyEvent, Never>`                     | Every event, on the main thread                 |
| `state: AnyPublisher<KindlyEventState, Never>`, `currentState` | The SDK state                                   |
| `isSupported`                                                  | Whether the chat can run here                   |
| `verboseLogging`, `enableCrashReporting`                       | Logging and crash reporting                     |
| `KindlyAuthCredentials(idToken:accessToken:issuer:expiresAt:)` | Credentials, at least one token                 |
| `KindlyWebViewError`                                           | The thrown errors                               |

### Android, `KindlyWebViewSDK`

| API                                                                                            | Purpose                                   |
| ---------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `start(application, botKey, languageCode, market, authTokenProvider, authCredentialsProvider)` | Start the SDK. The providers are optional |
| `launchChat(context, triggerDialogueId, languageCode)`, `displayChat(context)`                 | Show the chat                             |
| `close()`                                                                                      | Hide the chat, keep the conversation      |
| `endChat(): Deferred<Unit>`                                                                    | End the conversation                      |
| `kill(): Deferred<Unit>`                                                                       | End the conversation and stop the SDK     |
| `sendMessage(message, newContext): Deferred<Unit>`                                             | Send a message as the user                |
| `triggerDialogue(id)`                                                                          | Start a dialogue                          |
| `setNewContext(context)`, `clearNewContext()`                                                  | Context for the next message              |
| `authenticate(): Deferred<Unit>`, `deauthenticate(): Deferred<Unit>`                           | Sign the chat in and out                  |
| `` `interface`: KindlyWebViewInteraction? ``                                                   | Link interception                         |
| `events: Flow<KindlyEvent>`                                                                    | Every event                               |
| `state: Flow<KindlyEventState>`, `currentState`                                                | The SDK state                             |
| `isSupported(context)`                                                                         | Whether the chat can run here             |
| `verboseLogging`, `enableCrashReporting`                                                       | Logging and crash reporting               |
| `KindlyAuthTokenProvider`, `KindlyAuthCredentialsProvider`, `AuthTokenRequest`                 | Auth providers                            |
| `KindlyWebViewException`                                                                       | The failures of the `Deferred` results    |

### Flutter, `KindlyWebViewSDK`

| API                                                                                    | Purpose                       |
| -------------------------------------------------------------------------------------- | ----------------------------- |
| `start(botKey:, market:, languageCode:, authTokenCallback:, authCredentialsCallback:)` | Start the SDK                 |
| `displayChat(languageCode:, triggerDialogueId:)`, `launchChat(...)`                    | Show the chat                 |
| `closeChat()`, `endChat()`, `kill()`                                                   | Hide, end, stop               |
| `sendMessage(message, newContext:)`                                                    | Send a message as the user    |
| `triggerDialogue(dialogueId)`                                                          | Start a dialogue              |
| `setNewContext(context)`, `clearNewContext()`                                          | Context for the next message  |
| `authenticate()`, `deauthenticate()`                                                   | Sign the chat in and out      |
| `setDelegate(shouldHandleLink:)`, `clearDelegate()`                                    | Link interception             |
| `events`, `state`, `currentState()`                                                    | Events and state              |
| `isSupported()`                                                                        | Whether the chat can run here |
| `setVerboseLogging(enabled)`, `setCrashReporting(enabled)`                             | Logging and crash reporting   |
| `KindlyWebViewAuthCredentials`, `KindlyWebViewException`                               | Credentials and errors        |

### React Native, `KindlyWebViewSDK`

| API                                                                                      | Purpose                       |
| ---------------------------------------------------------------------------------------- | ----------------------------- |
| `start({ botKey, market, languageCode?, authTokenCallback?, authCredentialsCallback? })` | Start the SDK                 |
| `displayChat({ languageCode?, triggerDialogueId? })`, `launchChat(...)`                  | Show the chat                 |
| `closeChat()`, `endChat()`, `kill()`                                                     | Hide, end, stop               |
| `sendMessage(message, { newContext? })`                                                  | Send a message as the user    |
| `triggerDialogue(dialogueId)`                                                            | Start a dialogue              |
| `setNewContext(map)`, `clearNewContext()`                                                | Context for the next message  |
| `authenticate()`, `deauthenticate()`                                                     | Sign the chat in and out      |
| `setDelegate({ shouldHandleLink })`, `setDelegate(null)`                                 | Link interception             |
| `addEventListener(listener)`, `addStateListener(listener)`, `getState()`                 | Events and state              |
| `isSupported()`                                                                          | Whether the chat can run here |
| `setVerboseLogging(enabled)`, `setCrashReporting(enabled)`                               | Logging and crash reporting   |
| `KindlyWebViewError`                                                                     | The rejections, with a `code` |

***

## Verification

1. Build and run the app, then show the chat. It loads within a few seconds and shows the bot's greeting
2. Turn on the debug logs and watch them, see Logging and crash reporting. A refused value, such as a market in the wrong format, shows up there as an error
3. Subscribe to the state: `connectionState` goes from `none` to `loading` to `connected` when the chat page loads
4. Send a message in the chat and see the bot answer. With the chat open, a `message` event arrives
5. On iOS, attach a photo in the chat to prove the usage descriptions are in place

***

## Troubleshooting

| Symptom                                                                                    | Fix                                                                                                                                                                                            |
| ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Nothing happens when the chat should show, and the log says the call came before `start`   | `start` did not run, or refused its arguments, see Argument rules                                                                                                                              |
| The chat shows an error about 20 seconds after it opens, and `connectionState` is `failed` | The market slug is not one configured for the bot, or the bot's settings for that market lack the web chat's translations. Check the slug, then ask Kindly support with the bot key and market |
| The chat never connects on a bot that only allows listed domains                           | Add the app's iOS bundle id and Android package name to the bot's allowed domains                                                                                                              |
| The app quits when someone attaches a photo or saves an image on iOS                       | A usage description is missing from `Info.plist`, see Phase 3                                                                                                                                  |
| A notice says the chat is not supported                                                    | See Checking support. On iOS, check `WKAppBoundDomains`                                                                                                                                        |
| `sendMessage` fails with the chat-not-ready error                                          | Show the chat once first                                                                                                                                                                       |
| A link in the chat opens the browser instead of the app                                    | Set the link delegate. A custom scheme such as `myapp://` never reaches it, link to an `https` address instead                                                                                 |
| The chat stays anonymous                                                                   | Pass a provider to `start` on every call, and see the debugging steps in AUTH.md                                                                                                               |
| No `message` events for messages the user can see                                          | Message events come only while the chat is open, see EVENTS.md                                                                                                                                 |
| Gradle cannot resolve `ai.kindly:sdk-webview` in Flutter or React Native                   | See the `repositoriesMode` notes in Phase 3                                                                                                                                                    |
| `pod install` fails on the deployment target                                               | Set `platform :ios, '15.4'` or later in `ios/Podfile`                                                                                                                                          |
| Two types named `KindlyEvent` clash                                                        | See Next to the native Kindly SDK                                                                                                                                                              |

***

## Deep reference

| Topic                                                                                                       | Fetch with                                                                                       |
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Token and credentials providers on every platform, the token rules, timeouts, signing in and out, debugging | `Use curl to download, read and follow: https://docs.kindly.ai/sdk/web-view-sdk-skill-auth.md`   |
| Every event type and payload field on every platform, message event timing, state                           | `Use curl to download, read and follow: https://docs.kindly.ai/sdk/web-view-sdk-skill-events.md` |
| The link delegate on every platform, what opens outside the app, timeouts and limits                        | `Use curl to download, read and follow: https://docs.kindly.ai/sdk/web-view-sdk-skill-links.md`  |

## Additional resources

- [Guides](https://docs.kindly.ai/sdk/web-view-sdk)
- [API reference](https://docs.kindly.ai/sdk/web-view-sdk-api-reference)
- [Authentication reference (AUTH.md)](https://docs.kindly.ai/sdk/web-view-sdk-skill-auth.md)
- [Events reference (EVENTS.md)](https://docs.kindly.ai/sdk/web-view-sdk-skill-events.md)
- [Links reference (LINKS.md)](https://docs.kindly.ai/sdk/web-view-sdk-skill-links.md)
