Skip to content
Documentation menu

Mobile SDKs

Put ZebChat inside your own Android, iOS, Flutter or React Native app. Your users chat with your team without leaving the app, and your agents answer them from the same inbox as your website visitors.

What you get

  • The full chat in your app. A full-screen chat with every widget feature: your colors and greeting, forms, file and photo uploads, ratings, the AI assistant and your language settings.
  • App users in the visitors list. Agents see them live with a phone icon, your app id and version, the device, and the screens they open.
  • Your signed-in users. Pass their name and email, or a verified id so their conversations follow them across devices and your website.
  • Push notifications for agent replies while the app is in the background, sent through your own Firebase project. The SDKs do not depend on Firebase: your app forwards the token and the message.
  • An unread count for a badge on your help button.
PlatformPackageMinimum
Androidcom.zebchat:chat (Maven Central)minSdk 23, compileSdk 35
iOSZebChat (Swift Package Manager or CocoaPods)iOS 14, Swift 5.9
Flutterzebchat_flutter (pub.dev)Flutter 3.22, Dart 3.4
React Native@zebchat/react-native (npm)React Native 0.74

Set up in the dashboard

  1. Open Websites, pick the website whose chat you want in the app, and open the Mobile apps tab. Your site key and install snippets are there too.
  2. Add your app ids: the Android package name (applicationId) and the iOS bundle identifier. Apps that are not listed are refused and the chat says it is not available in this app.
  3. For push notifications, upload a Firebase service account JSON for the Firebase project your app uses (Firebase console → Project settings → Service accounts → Generate new private key). Then use Send test push with a device token. Turn off Show message text in notifications if you want pushes without a preview.

Your signed-in users

Every SDK has setUser and logout. Without an id, the name, email and phone are saved as unverified. With identity verification, pass your user id and a hash that your server computes: hex(HMAC-SHA256(identity secret, id)). Never put the identity secret in the app. It works exactly like the website setUser.

Node.js (your server)
import { createHmac } from 'node:crypto';

// The website's identity secret (Websites → your website → Security). Server only.
export function zebchatHash(userId) {
  return createHmac('sha256', process.env.ZEBCHAT_IDENTITY_SECRET)
    .update(String(userId), 'utf8')
    .digest('hex');
}

// Return it with your user's profile, e.g. { id: '42', name: 'Ana', zebchatHash: zebchatHash('42') }

Quick starts

Android · iOS · Flutter · React Native

Android (Kotlin or Java)

Gradle
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

// app/build.gradle.kts
dependencies {
    implementation("com.zebchat:chat:1.0.0")
}

Call configure in Application.onCreate, then open the chat from anywhere:

Kotlin
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        ZebChat.configure(this, siteKey = "zc_…", options = ZebChatOptions(locale = "auto"))
    }
}

// Anywhere, e.g. a "Help" button:
ZebChat.show(activity)
Kotlin · user, screens, unread
ZebChat.setUser(ZebChatUser(id = "42", email = "[email protected]", name = "Ana", hash = hashFromYourServer))
ZebChat.logout()   // on sign-out: removes this device's push registration, then forgets the visitor

ZebChat.trackScreen("Checkout")   // agents see app://<your app id>/Checkout (buffered offline, max 20)

val listener = ZebChat.addUnreadListener { count -> badge.text = count.toString() }
ZebChat.removeUnreadListener(listener)

Push: add Firebase Cloud Messaging to your app as usual, then forward tokens and messages from your FirebaseMessagingService (an app can only have one, so the SDK never declares its own):

Kotlin · push
class MyMessagingService : FirebaseMessagingService() {
    override fun onNewToken(token: String) {
        ZebChat.setPushToken(token)
    }

    override fun onMessageReceived(message: RemoteMessage) {
        if (ZebChat.isZebChatNotification(message.data)) {
            // App in the foreground: FCM does not display it, so the SDK does (channel zebchat_chat).
            ZebChat.showNotification(this, message.data)
            return
        }
        // … your own messages
    }
}

// At start (after configure), as onNewToken only fires on changes:
FirebaseMessaging.getInstance().token.addOnSuccessListener { ZebChat.setPushToken(it) }

// In your launcher Activity: a tap on a notification FCM displayed in the background.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ZebChat.handleNotificationTap(this, intent.extras)
}
override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    ZebChat.handleNotificationTap(this, intent.extras)
}

iOS (Swift)

Swift Package Manager
// Xcode → File → Add Package Dependencies…
https://github.com/zebchat/zebchat-ios

// or in Package.swift
.package(url: "https://github.com/zebchat/zebchat-ios", from: "1.0.0")
CocoaPods
pod 'ZebChat', '~> 1.0'

Add the camera and photo strings to your app’s Info.plist (see Permissions and privacy), then:

Swift
import ZebChat

// AppDelegate.application(_:didFinishLaunchingWithOptions:)
ZebChat.configure(siteKey: "zc_…")

// Open the chat (full screen, modal)
ZebChat.show(from: viewController)
Swift · user, screens, unread
// `hash` is computed on YOUR server; never put the identity secret in the app.
ZebChat.setUser(ZebChatUser(id: "42", email: "[email protected]", name: "Ana", hash: hashFromServer))
ZebChat.logout()   // sign-out: removes the push registration, then forgets the user and the visitor

ZebChat.trackScreen("Checkout")

let token = ZebChat.addUnreadListener { count in helpButton.badge = count }
ZebChat.removeUnreadListener(token)

Push: add FirebaseMessaging to your app, upload your APNs key (.p8) in Firebase → Project settings → Cloud Messaging, and enable the Push Notifications capability in Xcode.

Swift · push
import FirebaseCore
import FirebaseMessaging
import UserNotifications
import ZebChat

// In your AppDelegate (UNUserNotificationCenterDelegate, MessagingDelegate):
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    FirebaseApp.configure()
    ZebChat.configure(siteKey: "zc_…")
    Messaging.messaging().delegate = self
    UNUserNotificationCenter.current().delegate = self
    // Ask when it suits your app (the SDK never asks for permission itself).
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
    application.registerForRemoteNotifications()
    return true
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Messaging.messaging().apnsToken = deviceToken
}

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    if let fcmToken { ZebChat.setPushToken(fcmToken) }
}

// A push while the app is open: no banner while the chat is visible.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
                            withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    let userInfo = notification.request.content.userInfo
    if ZebChat.notificationReceived(userInfo) {
        completionHandler(ZebChat.isChatVisible ? [] : [.banner, .sound])
    } else {
        completionHandler([.banner, .sound, .badge])
    }
}

// A tapped push opens the chat (also after a cold start).
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {
    if let root = window?.rootViewController {
        ZebChat.handleNotificationTap(response.notification.request.content.userInfo, from: root)
    }
    completionHandler()
}

Flutter

Terminal
flutter pub add zebchat_flutter

# iOS: in ios/Podfile set  platform :ios, '14.0'  then
cd ios && pod install

Android apps need minSdk = 23 or higher. Add the Info.plist strings to ios/Runner/Info.plist.

Dart
import 'package:flutter/material.dart';
import 'package:zebchat_flutter/zebchat_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ZebChat.configure(siteKey: 'zc_your_site_key'); // locale: 'auto' by default
  runApp(const MyApp());
}

// Anywhere, for example a "Help" button:
ElevatedButton(onPressed: ZebChat.show, child: const Text('Chat with us'));
Dart · user, screens, unread
await ZebChat.setUser(ZebChatUser(
  id: user.id,
  email: user.email,
  name: user.name,
  hash: user.zebchatHash, // from your server
));
await ZebChat.logout(); // on sign-out

ZebChat.trackScreen('Checkout'); // or a NavigatorObserver that tracks named routes

StreamBuilder<int>(stream: ZebChat.unreadCount, initialData: 0, builder: …);

Push with firebase_messaging (on iOS also enable Background Modes → Remote notifications). Call it after configure:

Dart · push
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:zebchat_flutter/zebchat_flutter.dart';

Future<void> setUpZebChatPush() async {
  final messaging = FirebaseMessaging.instance;
  await messaging.requestPermission(); // your app decides when to ask

  final token = await messaging.getToken();
  if (token != null) await ZebChat.setPushToken(token);
  messaging.onTokenRefresh.listen(ZebChat.setPushToken);

  // Android: FCM does not display notifications while the app is in the foreground.
  FirebaseMessaging.onMessage.listen((message) {
    if (ZebChat.isZebChatNotification(message.data)) {
      ZebChat.showNotification(message.data);
    }
  });

  // Tapped while in the background, or the tap that launched the app.
  FirebaseMessaging.onMessageOpenedApp.listen((message) {
    ZebChat.handleNotificationTap(message.data);
  });
  final initial = await messaging.getInitialMessage();
  if (initial != null) await ZebChat.handleNotificationTap(initial.data);
}

React Native

Terminal
npm install @zebchat/react-native
# iOS
cd ios && pod install

Android needs nothing else: com.zebchat:chat comes from Maven Central. Add the Info.plist strings on iOS.

TSX
import { useEffect } from 'react';
import { Button } from 'react-native';
import { ZebChat, useZebChatUnread } from '@zebchat/react-native';

export function App() {
  useEffect(() => {
    ZebChat.configure({ siteKey: 'zc_your_site_key' });
  }, []);

  const unread = useZebChatUnread();
  return <Button title={`Chat with us (${unread})`} onPress={() => ZebChat.show()} />;
}
TypeScript · user, screens
// `hash` is the HMAC your server computes for identity verification.
ZebChat.setUser({ id: user.id, email: user.email, name: user.name, hash: user.zebchatHash });
ZebChat.logout(); // removes this device's push token, then forgets the user and the visitor

ZebChat.trackScreen('Checkout');

Push with @react-native-firebase/messaging:

TypeScript · push
import { PermissionsAndroid, Platform } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import { ZebChat } from '@zebchat/react-native';

export async function setUpZebChatPush() {
  if (Platform.OS === 'android' && Platform.Version >= 33) {
    await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
  } else {
    await messaging().requestPermission();
  }

  ZebChat.setPushToken(await messaging().getToken());
  messaging().onTokenRefresh((token) => ZebChat.setPushToken(token));

  // App in the foreground: Android shows it on the zebchat_chat channel.
  messaging().onMessage(async (message) => {
    if (ZebChat.isZebChatNotification(message.data)) ZebChat.showNotification(message.data);
  });

  // Tapped while in the background, or the tap that launched the app.
  messaging().onNotificationOpenedApp((message) => ZebChat.handleNotificationTap(message.data));
  const initial = await messaging().getInitialNotification();
  if (initial) ZebChat.handleNotificationTap(initial.data);
}

How push notifications work

  • ZebChat sends a push when an agent (or the AI assistant) replies while the user is not in the chat: the app is in the background or closed. The chat disconnects as soon as it is hidden, so the user counts as offline right away.
  • Pushes go through your Firebase project (FCM for Android and iOS; Firebase forwards to APNs). They carry only {zebchat, conversationId, siteKey, title, body}, no visitor data. On Android they use the channel zebchat_chat; on iOS the badge is the unread count.
  • A user who also has your website open in a browser tab counts as online and gets no push while that tab is open.

Permissions and privacy

  • Android 13+ notifications: the SDK never asks for POST_NOTIFICATIONS. Declare and request it yourself, when it suits your app. The SDK declares only INTERNET; if your app declares and holds CAMERA, the file picker also offers “take a photo”.
  • iOS Info.plist (required): the photo picker opens from the chat, so your app must explain camera and photo access, or iOS closes the app when the picker opens.
Info.plist
<key>NSCameraUsageDescription</key>
<string>Take a photo to send in the chat.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose photos to send in the chat.</string>
  • Data the SDKs send to ZebChat: what you pass to setUser (name, email, phone, user id); messages, photos and files the user sends; screen names from trackScreen; a random visitor key and the push token; app id and version, SDK version, OS version, device model and type. No advertising ID, no tracking, nothing shared beyond your own Firebase project for pushes.
  • The visitor key stays in the Keychain (iOS) or private storage excluded from backups (Android) and never enters the chat page or a URL. The iOS SDK ships a privacy manifest; each README lists the answers for the App Store and Google Play data forms.

FAQ

Does it work with Expo?

Yes, in a development build after npx expo prebuild. No config plugin is needed. Expo Go is not supported because it cannot load native modules.

What happens offline?

The chat shows that it is offline and reconnects on its own when the network comes back. Screen views are buffered (up to 20) and sent later, and sessions are cached so a cold start rarely waits for the network.

Which plans include the mobile SDKs?

All of them, push notifications included. Identity verification (the verified id and hash) is a plan feature, as on the website: see Pricing.

Does the app chat use my widget settings?

Yes. The SDKs show ZebChat’s own full-screen chat with the website’s widget settings, so changes and new widget features reach your app without an app update.