ऐप के अंदर चैट (WebView)
WebView के ज़रिए किसी भी मोबाइल या डेस्कटॉप ऐप में पूरा चैट विजेट एम्बेड करें, सभी फ़ीचर्स और अपने-आप अपडेट के साथ।
नेटिव ऐप में कस्टमर सपोर्ट जोड़ने का सबसे तेज़ तरीका है हमारे होस्ट किए गए फ़ुल-स्क्रीन विजेट पेज को WebView में लोड करना। आपको बिल्कुल वही विजेट मिलता है जो वेबसाइट पर है: होम, संदेश, हेल्प सेंटर, सर्च, इमोजी, अटैचमेंट, टाइमस्टैम्प, रीड रसीदें और बातचीत बंद करना, और यह अपने-आप अपडेट होता है। एक बार इंटीग्रेट करने के बाद, विजेट में बदलाव के लिए ऐप को कभी नई रिलीज़ की ज़रूरत नहीं पड़ती।
क्विक स्टार्ट
WebView में यह URL खोलें। लॉग-इन उपयोगकर्ता के लिए ज़रूरी है कि आप externalId और email (और हो सके तो name भी) शामिल करें, वरना एजेंट को हमेशा सिर्फ़ एक अनाम विज़िटर दिखेगा। हर वैल्यू को URL-encode करें:
https://<your-domain>/api/widget-embed?appId=YOUR_APP_ID&externalId=USER_ID&email=USER_EMAIL&name=USER_NAMEअनाम रूप सिर्फ़ तब इस्तेमाल करें, जब उपयोगकर्ता लॉग-इन न हो:
https://<your-domain>/api/widget-embed?appId=YOUR_APP_ID⚠️ एजेंट को बिना ईमेल वाला “अनाम विज़िटर” दिख रहा है? यह इंटीग्रेशन की सबसे आम गलती है: आपके WebView URL में email/externalId नहीं है। सर्वर इन्हें खुद से नहीं बना सकता, ऐप को इन्हें भेजना होगा। अगर विज़िटर की ID anon-… जैसी दिखती है, तो कुछ भी नहीं भेजा गया।
JavaScript / कोई भी WebView (Flutter के अलावा)
Electron, डेस्कटॉप शेल, React Native, या नेटिव WKWebView जहाँ URL आप खुद सेट करते हैं, वहाँ URL को JS में बनाएँ। URLSearchParams हर वैल्यू को आपके लिए अपने-आप encode कर देता है:
// Build the support URL for the signed-in user before opening the WebView.
function buildSupportUrl(domain, user) {
const params = new URLSearchParams({
appId: 'YOUR_APP_ID',
platform: 'windows', // Strongly recommended: decides which help articles this visitor sees
// ios / macos_appstore / macos / android / windows / web
// locale: userSelectedLanguage, // Optional: only if your app has its own language switch; also add forceLocale: '1'
// Omitted = follow the device language (covers the App Store's 50 localizations)
// — Always pass these for signed-in users, or agents only see an "anonymous visitor" —
externalId: user.id, // The user's unique ID in your system
email: user.email, // The user's email (important)
name: user.name || '', // The user's name (optional)
// hmac: user.hmac, // Optional: computed on your server to prevent impersonation (see HMAC below)
// attrs: JSON.stringify({ plan: user.plan, expiresAt: user.expireAt }), // Optional: custom attributes
});
return `https://${domain}/api/widget-embed?${params.toString()}`;
}
// Usage: load the returned url in your WebView (instead of the old ?appId=...-only URL).
const url = buildSupportUrl('askais.com', currentUser);
myWebView.loadURL(url); // Electron: win.loadURL(url); native: load this URLFlutter (webview_flutter)
pubspec.yaml में webview_flutter: ^4.x जोड़ें, फिर:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class SupportPage extends StatefulWidget {
const SupportPage({super.key});
@override
State<SupportPage> createState() => _SupportPageState();
}
class _SupportPageState extends State<SupportPage> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
// For signed-in users always include externalId + email, or agents see an "anonymous visitor" (no name / email).
// Pass only appId + locale when the user is not signed in. Uri.https URL-encodes the values.
final uri = Uri.https('askais.com', '/api/widget-embed', {
'appId': 'YOUR_APP_ID',
'platform': Platform.isIOS ? 'ios' : 'android', // Strongly recommended: decides which help articles are visible
// No locale = follow the device language (covers the App Store's 50 localizations).
// Only pass it if your app has its own language switch, together with 'forceLocale': '1':
// 'locale': appSettings.selectedLanguage,
'externalId': user.id, // The user's unique ID in your system (important)
'email': user.email, // The user's email (important)
'name': user.name, // Optional
// 'hmac': hmacFromYourServer, // Optional: computed on your server to prevent impersonation
});
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(Colors.white)
..loadRequest(uri);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Support')),
// Full-screen: drop the appBar and use your own close button.
body: SafeArea(child: WebViewWidget(controller: _controller)),
);
}
}
// Open it from wherever your "Support" button lives:
// Navigator.push(context,
// MaterialPageRoute(builder: (_) => const SupportPage()));दूसरे स्टैक में iOS/Android WebView के लिए (Swift WKWebView, Kotlin WebView, React Native react-native-webview) तरीका बिल्कुल वही है: URL लोड करें और JavaScript चालू करें।
नेटिव iOS और Android
कोई SDK इंस्टॉल नहीं करना है, आप वही URL बनाकर WebView को दे देते हैं। Mac के मामले पर ध्यान दें: App Store बिल्ड और DMG बिल्ड एक ही ऑपरेटिंग सिस्टम पर चलते हैं, इसलिए यह वैल्यू रनटाइम जाँच से नहीं, बल्कि बिल्ड फ़्लैग से आनी चाहिए।
// ── iOS (Swift / WKWebView) ────────────────────────────────
// On Mac Catalyst / macOS, set platform to macos_appstore or macos.
#if targetEnvironment(macCatalyst)
let platform = "macos_appstore" // App Store build; a DMG build from your website passes "macos"
#else
let platform = "ios" // iPhone and iPad are both ios
#endif
var comps = URLComponents(string: "https://\(domain)/api/widget-embed")!
comps.queryItems = [
.init(name: "appId", value: "YOUR_APP_ID"),
.init(name: "platform", value: platform),
.init(name: "externalId", value: user.id), // Required for signed-in users
.init(name: "email", value: user.email), // Required for signed-in users
.init(name: "name", value: user.name),
// Only pass these two if your app has its own language switch:
// .init(name: "locale", value: settings.language),
// .init(name: "forceLocale", value: "1"),
]
webView.load(URLRequest(url: comps.url!)) // URLComponents URL-encodes the values
// ── Android (Kotlin / WebView) ─────────────────────────────
val url = Uri.parse("https://$domain/api/widget-embed")
.buildUpon()
.appendQueryParameter("appId", "YOUR_APP_ID")
.appendQueryParameter("platform", "android")
.appendQueryParameter("externalId", user.id)
.appendQueryParameter("email", user.email)
.appendQueryParameter("name", user.name)
// .appendQueryParameter("locale", settings.language)
// .appendQueryParameter("forceLocale", "1")
.build()
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true // The widget uses localStorage to remember the visitor
webView.loadUrl(url.toString())URL पैरामीटर
appId(ज़रूरी): आपके इनबॉक्स की App ID।locale: इंटरफ़ेस की भाषा। लगभग हर स्थिति में इसे न भेजें; तब विजेट WebView द्वारा बताई गई डिवाइस भाषा अपनाता है, और उपयोगकर्ता यही उम्मीद करते हैं। इंटरफ़ेस App Store Connect के सभी 50 लोकलाइज़ेशन में अनुवादित है; इस सूची से बाहर की कोई भी भाषा अंग्रेज़ी पर लौट आती है। इसे सिर्फ़ तब भेजें जब आपके ऐप में अपना इन-ऐप भाषा स्विचर हो, और तब उपयोगकर्ता की चुनी हुई भाषा के साथforceLocale=1भी भेजें ताकि वह डिवाइस सेटिंग पर प्राथमिकता पाए। कभी भी कोई वैल्यू हार्ड-कोड न करें: हार्ड-कोड किया गयाzh-Hansही वह वजह है जिससे किसी जापानी ऐप में चीनी विजेट दिखने लगता है।platform(अत्यधिक अनुशंसित): विज़िटर किस बिल्ड पर है। इसी से तय होता है कि उसे कौन-से हेल्प आर्टिकल दिखेंगे और AI, App Store के नियमों के तहत जवाब देगा या नहीं। इसे न भेजें तो प्लेटफ़ॉर्म का अंदाज़ा WebView के User-Agent से लगाया जाता है: इससे iPhone, Android और Windows पहचाने जाते हैं, लेकिन Mac App Store बिल्ड और DMG बिल्ड में फ़र्क नहीं हो पाता, और डेस्कटॉप मोड वाला iPad भी नहीं पहचाना जाता। वैल्यू:ios(iPhone और iPad),macos_appstore(Mac App Store बिल्ड),macos(सीधा DMG बिल्ड),android,windows,web।externalId: आपके ऐप में उपयोगकर्ता की यूनिक ID। इसे भेजने से एजेंट उपयोगकर्ता को पहचान पाता है और उसकी पिछली बातचीत एक साथ जुड़ जाती है।email,name,avatarUrl: एजेंट को दिखने वाली प्रोफ़ाइल जानकारी।hmac: पहचान सिग्नेचर (नीचे देखें)। वैकल्पिक।
प्लेटफ़ॉर्म के हिसाब से कंटेंट छिपाना (App Store अनुपालन)
App Store का नियम 3.1.1 किसी iOS ऐप को बाहरी खरीदारी, सब्सक्रिप्शन या रेफ़रल से जुड़ा कंटेंट दिखाने की अनुमति नहीं देता। इसलिए हर हेल्प आर्टिकल और हर FAQ में प्लेटफ़ॉर्म के हिसाब से दिखने की सेटिंग होती है: अपने डैशबोर्ड में हेल्प सेंटर या “अक्सर पूछे जाने वाले प्रश्न” पेज खोलें और उस आइटम पर प्लेटफ़ॉर्म चिप्स चालू या बंद करें। चुने गए प्लेटफ़ॉर्म के विज़िटर उसे सूची में नहीं देखते और सीधे लिंक से भी नहीं खोल पाते।
AI हेल्प आर्टिकल या FAQ से जवाब नहीं देता, बल्कि उस कंटेंट से देता है जो आपने “Training” में जोड़ा है। वहाँ की फ़ाइलों और सवाल-जवाब जोड़ियों पर भी यही प्लेटफ़ॉर्म चिप्स होते हैं, और किसी प्लेटफ़ॉर्म के लिए छिपाया गया आइटम उस प्लेटफ़ॉर्म के विज़िटर को दिए जाने वाले जवाबों में इस्तेमाल नहीं होता। अगर AI को भी उसका ज़िक्र नहीं करना चाहिए, तो उसे दोनों जगह छिपाएँ।
यह भरोसेमंद ढंग से काम करेगा या नहीं, यह दो बातों पर निर्भर है। पहली, आपके ऐप को ख़ुद platform भेजना चाहिए; User-Agent से अंदाज़ा लगाना सिर्फ़ बैकअप है। दूसरी, वैल्यू सिर्फ़ ऑपरेटिंग सिस्टम को नहीं, बल्कि बिल्ड को दर्शाए:
- iPad अलग नहीं है। यह उन्हीं नियमों के तहत वही iOS ऐप चलाता है, इसलिए
iosमें iPhone और iPad दोनों आ जाते हैं। - Mac के दो बिल्ड होते हैं। Mac App Store बिल्ड नियम 3.1.1 से बंधा है; आपकी अपनी साइट से दिया जाने वाला DMG नहीं। दोनों का ऑपरेटिंग सिस्टम एक ही है, इसलिए रनटाइम पर कोई भी चीज़ इनमें फ़र्क नहीं कर सकती; यह बिल्ड के समय तय करें (उदाहरण के लिए Xcode फ़्लैग से) और उसी के अनुसार
macos_appstoreयाmacosभेजें। गलती हुई तो या तो स्टोर बिल्ड रिव्यू के दौरान सब्सक्रिप्शन कंटेंट दिखा देगा, या DMG बिल्ड बेवजह आपका आधा हेल्प सेंटर छिपा देगा।
बदलाव तुरंत लागू होते हैं: यह सेटिंग ऐप में नहीं, आपके डैशबोर्ड में रहती है, इसलिए क्या दिखे यह बदलने के लिए कभी नई रिलीज़ नहीं करनी पड़ती।
सत्यापित पहचान (HMAC, वैकल्पिक)
यह साबित करने के लिए कि कोई externalId सचमुच आपका लॉग-इन उपयोगकर्ता है (और किसी और का रूप धरने से रोकने के लिए), HMAC सिग्नेचर भेजें। इसे अपने सर्वर पर कैलकुलेट करें, सीक्रेट की को कभी ऐप में न रखें।
hmac = HMAC-SHA256(secretKey, externalId), आउटपुट छोटे अक्षरों वाले hex में।- पेलोड
externalIdहै, इसलिए इसे हमेशा भेजें: इसके बिना विजेट अपनी गुमनाम ID इस्तेमाल करता है, और सिर्फ़emailपर बना सिग्नेचर अस्वीकार हो जाता है। secretKeyआपके इनबॉक्स की सीक्रेट की है (हर इनबॉक्स की एक, इनबॉक्स सेटिंग्स से लें)।hmacके बिना विज़िटर को असत्यापित माना जाता है।
import { createHmac } from 'node:crypto';
// SECRET_KEY lives only on your server, never ship it inside the app.
const hmac = createHmac('sha256', SECRET_KEY)
.update(externalId) // exactly the externalId you pass to the widget
.digest('hex');
// Return { externalId, email, hmac } to the app, which appends them to the URL.प्लेटफ़ॉर्म से जुड़े नोट्स
- Android:
INTERNETअनुमति बनाए रखें; बैक बटन को इंटरसेप्ट करें ताकि वह पहले WebView के अंदर ही पीछे जाए।webview_flutterके हाल के वर्ज़न अटैचमेंट के लिए<input type="file">सपोर्ट करते हैं। - iOS: HTTPS बिना किसी ATS अपवाद के काम करता है। इमेज अपलोड विजेट का बिल्ट-इन फ़ीचर है:
WKWebViewखुद ही नेटिव फ़ोटो पिकर खोल देता है, इसलिए आपको कुछ बनाना नहीं पड़ता। फ़ोटो लाइब्रेरी से चुनने के लिए किसी अनुमति की ज़रूरत नहीं।Info.plistमेंNSCameraUsageDescriptionसिर्फ़ तब चाहिए जब आपको कैमरे वाला “फ़ोटो लें” विकल्प चाहिए; इसके बिना उपयोगकर्ता के उस विकल्प पर टैप करते ही ऐप क्रैश हो जाता है।
नेटिव UI चाहिए?
अगर आपको WebView के बजाय खुद बनाया गया नेटिव चैट इंटरफ़ेस चाहिए, तो मोबाइल SDK पेज पर दिए गए सिर्फ़-लॉजिक क्लाइंट इस्तेमाल करें। ज़्यादातर ऐप्स के लिए ऊपर वाला WebView तरीका जल्दी लॉन्च होता है और हमेशा सभी फ़ीचर्स के साथ आता है।