React Native SDK
The official Kindly Chat SDK for React Native â a thin TurboModule wrapper around the public iOS xcframework and Android AAR. The native binaries ship inside the npm tarball, so customers don't need Swift Package Manager, Maven Central, or any extra Gradle config.
Installation
yarn add @kindly-ai/react-native
cd ios && pod installThat's it â no extra setup. The bundled iOS framework is pre-signed with the Apple Distribution cert and passes App Store ITMS-91065 validation.
Usage
import { KindlySDK, hexColor } from '@kindly-ai/react-native';
// 1. Initialize once on app start
KindlySDK.start({
botKey: 'your-bot-key',
market: 'general', // required â your market slug (Settings â General â Details & markets)
languageCode: 'en',
// Optional: invoked by the SDK when it needs an authenticated JWT for
// the current chat session. `chatId` is the backend-generated identifier
// â include it when calling your auth backend so the token is scoped to
// this chat. Return null for anonymous chats.
authTokenCallback: async (chatId) => {
return await yourAuthBackend.getKindlyJWT({ chatId });
},
});
// Identity providers that issue more than a single JWT - a companion access
// token, an issuer, or an opaque token whose expiry only you know - use
// authCredentialsCallback instead. It wins when both are given.
KindlySDK.start({
botKey: 'YOUR_BOT_KEY',
market: 'general',
authCredentialsCallback: async (chatId) => {
const tokens = await yourAuthBackend.getTokens({ chatId });
return {
idToken: tokens.idToken, // optional
accessToken: tokens.accessToken, // optional
issuer: 'https://idp.example.com/', // optional
expiresAt: tokens.expiresAtUnixSeconds, // optional, Unix seconds
};
},
});
// Only an access token? Return only that. It becomes the bearer.
KindlySDK.start({
botKey: 'YOUR_BOT_KEY',
market: 'general',
authCredentialsCallback: async () => ({
accessToken: await yourAuthBackend.getAccessToken(),
issuer: 'https://idp.example.com/',
}),
});
// 2. Open the chat UI when the user wants it
<Button title="Open chat" onPress={() => KindlySDK.launchChat()} />
// 3. (Optional) host-app callbacks for buttons / link interception
KindlySDK.setDelegate({
onButtonPressed: (button, chatLog) => console.log('button', button),
shouldHandleLink: (url) => {
if (url.startsWith('myapp://')) {
// Take over the deep link
return false;
}
return true; // let the SDK open it
},
});API
Lifecycle
Method | Purpose |
|---|---|
start({botKey, market, languageCode?, authTokenCallback?, authCredentialsCallback?}) | Initialize the SDK. Must be called before any other method. market is required (Markets feature); the SDK loads that market's settings with no fallback. Use authCredentialsCallback for identity providers that issue more than a single JWT. |
displayChat({languageCode?, triggerDialogueId?}) | Show the chat UI. |
launchChat({triggerDialogueId?}) | Connect (if needed) and show chat. Recommended on Android. |
closeChat() | Dismiss the UI without ending the session. |
endChat() | End the chat session: clears messages, token, dismisses UI. |
kill() | Tear the SDK down completely; call start() again before reuse. |
setLanguage(code) | Update the language for the current session. |
Authentication
Method | Purpose |
|---|---|
authenticate() | Re-authenticate the live chat with fresh credentials. For a sign-in mid-session; expiry is handled by the SDK on its own. |
deauthenticate() | Sign out of the chat without ending it. History is kept, the chat continues anonymously. |
Every field of KindlyAuthCredentials is optional, but set at least one of idToken / accessToken. Whichever you supply becomes the Authorization: Bearer, id token first. A provider that issues only an access token is a normal, supported case, not a workaround. Credentials with neither token cannot authenticate and the chat stays anonymous.
Send accessToken alongside idToken only when the two really are a pair from the same provider. The backend verifies the companion against the id token's at_hash and rejects a mismatch, so passing the same value in both fields fails an auth that would otherwise have succeeded. The SDK drops a companion that duplicates the id token rather than sending it.
The SDK refreshes credentials 30 seconds before they expire, using whichever is earliest of the backend's reported expiry, your expiresAt, and the exp claim of the bearer. If none is known the credentials are treated as non-expiring - a trap for opaque tokens, so set expiresAt there. A token the SDK cannot parse means the expiry is unknown, never that the token is expired.
deauthenticate() is safe to call when the chat is already anonymous. The local credentials are cleared even if the backend call fails, so a sign-out is never left half done, but the promise still rejects so you know the backend did not hear about it.
Messaging
Method | Purpose |
|---|---|
sendMessage(text, {newContext?}) | Send a message programmatically. Returns the reconciled message. |
setNewContext({...}) / clearNewContext() | Stage / clear context attached to the next message. |
triggerDialogue(id) / clearTriggerDialogue() | Trigger a specific dialogue by id. |
handleUrl(url) | Handle a kindly://chat/dialogue/{id} deep link. Returns true if the URL was handled. |
callHandover(callback) | Initiate a handover to a human agent. |
Theme
Method | Purpose |
|---|---|
setCustomTheme({âĻ}) | Apply a custom theme. Pass undefined fields to keep defaults. |
clearCustomTheme() | Revert to the default / API-supplied theme. |
hexColor(hex) | Helper: #RRGGBB / #AARRGGBB â ARGB int (used in the theme). |
Push
Method | Purpose |
|---|---|
setAPNSDeviceToken(token) | iOS only â register the APNS device token. |
saveNotificationToken(token) | Android only â register the FCM token. |
handleNotification(data) | Forward an incoming push payload to the SDK. |
isKindlyNotification(data) | Returns true if the given push originated from Kindly. |
State / settings
Method | Purpose |
|---|---|
isChatDisplayed() | Returns true if the chat UI is currently visible (iOS only â Android always false). |
saveAuthToken(token) | Save a JWT auth token (iOS keychain). |
setVerboseLogging(enabled) | Toggle verbose SDK logging. |
setCrashReporting(enabled) | Toggle Sentry crash reporting from inside the SDK. |
Delegate
Method | Purpose |
|---|---|
setDelegate({onButtonPressed?, shouldHandleLink?, shouldHandleNotification?}) | Register host-app callbacks. |
clearDelegate() | Remove all delegate callbacks. |
shouldHandleLink and shouldHandleNotification are awaited synchronously by the native SDK with a 5-second timeout. After timeout the SDK falls back to its default true (handle normally) so chat doesn't hang on a slow JS callback.
Compatibility
- React Native: 0.76+ (New Architecture only)
- iOS: 15.0+
- Android: API level 24+ (Android 7.0)
License
MIT