Custom Integration Guide
Discover in your app, chat in the SDK
List the Discover enterprises in your own native UI, put whatever button you like on each row, and deep-link straight into that channel's SDK chat. Three calls, Android and iOS. No custom build, no new backend endpoint โ and the SDK keeps its usual Discover ยท Chats ยท Settings tabs.
Available on Android and iOS. This is the same VIH Messenger SDK from the base integration guide โ it adds a public VihDiscover facade. Android needs vih-sdk-staging 1.1.4+; iOS the matching SwiftPM package. Drop-in helpers live in custom-sdk/android/VihDiscoverKit.kt and custom-sdk/ios/VihDiscoverKit.swift.
๐งญ The shape of it
You keep the discovery experience in your app and hand off to the SDK only for the actual conversation:
- Your app renders the enterprise list (your layout, your button, your search).
- The SDK owns auth, the chat screen, real-time messaging and push โ unchanged.
There is no new API to build: the enterprise list is the same GET main/enterprises/?channel_id=<hashcode> the Discover tab already calls, surfaced as a clean SDK method that returns a flat VihEnterprise list.
๐ฉ How it works โ three calls
| Step | Call | What happens |
|---|---|---|
| 1 ยท Sign in | prepareSession(phone, hashcode) | Passwordless phone sign-in; stores the session token so the list & chat are authenticated โ before any SDK screen is shown. |
| 2 ยท List | fetchAllEnterprises(hashcode) | Returns every Discover enterprise (auto-paginated) as VihEnterprise. Render it in your own UI. |
| 3 ยท Open chat | openChat(โฆ, enterprise) | Deep-links into that enterprise's SDK chat screen โ your row's button action. Backing out of the chat lands on the SDK's main page (Discover ยท Chats ยท Settings), then back again returns to your app. |
Two layers, use whichever fits. VihDiscover is the stable public facade inside the SDK. VihDiscoverKit is a small drop-in helper (in this folder) you copy into your app for lambda / async callbacks and the fetchAllEnterprises auto-pagination convenience โ it just delegates to VihDiscover, so it's optional.
๐๏ธ The VihEnterprise model
Each row you render is a VihEnterprise. Use enterpriseId as the id you pass to openChat; raw is the full underlying model if you need a field not surfaced here.
enterpriseId : String // the id to pass to openChat (enterprise user_id) name : String // resolved display name logoUrl : String? // logo/avatar URL (may be null โ show a placeholder) category : String industry : String description : String? // short blurb, when the channel provides one raw : EnterPriseModel // full underlying model (escape hatch)
Rendering rows. Show each enterprise's logoUrl as a circular avatar before the name โ the same way the SDK's Discover tab does โ and overlay a small VIH Messenger logo badge at its bottom-right corner to signal it's a VIH channel. logoUrl may be null; show a placeholder then.
Android Kotlin
Copy custom-sdk/android/VihDiscoverKit.kt into your app (rename its package), or call com.vihmessenger.vihchatbot.discover.VihDiscover directly. All callbacks arrive on the main thread.
Sign the user in
Call once โ e.g. when your Discover screen opens. phone is digits only, with country code, no +.
import com.example.vihdiscover.VihDiscoverKit
VihDiscoverKit.prepareSession(
context = this,
phone = "919876543210",
hashcode = "your-channel-hashcode",
onReady = { loadEnterprises() },
onError = { e -> showError(e.message) }
)
List the enterprises in your own UI
fetchAllEnterprises walks every page and hands you the full list. Bind it to your own RecyclerView / Compose list.
private fun loadEnterprises() {
VihDiscoverKit.fetchAllEnterprises(
hashcode = "your-channel-hashcode",
onResult = { enterprises -> // List<VihEnterprise>
adapter.submitList(enterprises) // your adapter, your layout
},
onError = { e -> showError(e.message) }
)
}
// Prefer to page lazily instead? Use one page at a time:
// VihDiscoverKit.listEnterprises(hashcode, page = 1, search = "", industries = "", โฆ)
Open the chat from your button
In your row's click listener, hand the tapped VihEnterprise to openChat โ the SDK presents its chat screen for that channel.
holder.chatButton.setOnClickListener {
VihDiscoverKit.openChat(
context = holder.itemView.context,
hashcode = "your-channel-hashcode",
enterprise = item // the VihEnterprise for this row
)
}
Back navigation. On Android, openChat places the SDK dashboard (Discover ยท Chats ยท Settings) underneath the chat, so the flow is your list โ chat โ back โ SDK dashboard โ back โ your app. This is on by default (landOnDashboard = true); it needs the phone from prepareSession. Pass landOnDashboard = false to open the chat standalone (back returns straight to your list).
Calling the facade directly? The same three methods exist on VihDiscover with a Callback<T> interface: VihDiscover.prepareSession(context, phone, hashcode, cb), VihDiscover.listEnterprises(hashcode, page, search, industries, cb), and VihDiscover.openChat(context, hashcode, enterprise).
iOS Swift
Copy custom-sdk/ios/VihDiscoverKit.swift into your target, or call VihDiscover directly. Both async and completion-handler forms are provided; here's the async flow.
Sign in & list
import VihChatBotSDK
func loadDiscover() {
Task {
do {
try await VihDiscoverKit.prepareSession(
phone: "919876543210", hashcode: "your-channel-hashcode")
let enterprises = try await VihDiscoverKit.fetchAllEnterprises(
hashcode: "your-channel-hashcode") // [VihEnterprise]
await MainActor.run { self.render(enterprises) } // your own UI
} catch {
await MainActor.run { self.showError(error) }
}
}
}
Open the chat from your button
@objc func onChatTapped(_ enterprise: VihEnterprise) {
VihDiscoverKit.openChat(
from: self,
hashcode: "your-channel-hashcode",
enterprise: enterprise) // pushed onto your nav controller
}
// Land on the SDK dashboard after backing out of the chat (opt-in on iOS):
// VihDiscover.openChat(from: self, hashcode: hashcode,
// enterprise: enterprise, landOnDashboard: true)
Back navigation differs on iOS. iOS navigation is a UITabBarController, not an activity stack, so landOnDashboard defaults to false here (Android defaults to true). With the default, backing out of the chat returns to your list. Set landOnDashboard: true to present the SDK dashboard (Discover ยท Chats ยท Settings) and push the chat on top โ then your app dismisses that dashboard to return to the host. Verified on Android; smoke-test the iOS path in your app.
Prefer callbacks over async? Every method has a completion form, e.g. VihDiscoverKit.fetchAllEnterprises(hashcode:) { result in โฆ } โ delivered on the main thread.
๐๏ธ The floating widget on your bottom nav
The SDK's FloatingButtonView sits centered on your app's bottom navigation bar as the always-visible launcher into the SDK โ the standard entry point alongside your Discover list. Drop it into the middle slot of your bottom bar:
- Set your own artwork with
setCenterImageResource(...)+imageOnly = true(full-bleed, no background circle). - It pops (animates up) automatically when the SDK receives a message (
popOnMessageEnabled/popUp()). - Shows an unread-count badge via
unreadCountโ auto-increments per message and clears on tap; drive it yourself withcountUnreadOnMessage = false. - By default a tap opens the SDK; set
launchesSdkOnClick = false+ your ownsetOnClickListenerto handle it.
val widget = FloatingButtonView(context).apply {
setCenterImageResource(R.drawable.ic_vih_widget) // your widget artwork
imageOnly = true // full-bleed, no background circle
launchesSdkOnClick = false
setOnClickListener {
FloatingButtonView.startSdk(context, "919876543210", "your-channel-hashcode")
}
}
// place `widget` in the center slot of your bottom navigation bar;
// it pops + updates its unread badge automatically as messages arrive.
These are the same FloatingButtonView capabilities documented in the base integration guide.
๐ Search & pagination
The enterprise endpoint is paged and searchable server-side:
- Search: pass
search = "acme"to filter by name. - Industry filter: pass
industries = "Banking,Retail"(comma-separated). - Paging:
fetchAllEnterprisesauto-walks pages until one returns empty (capped atmaxPages, default 20). To page lazily yourself, calllistEnterprises(page = n, โฆ)and stop when a page comes back empty.
VihDiscoverKit.fetchAllEnterprises(
hashcode = "your-channel-hashcode",
search = "bank",
industries = "Banking,Fintech",
onResult = { list -> adapter.submitList(list) },
onError = { e -> showError(e.message) }
)
๐ Auth & session โ read this once
- Sign in first. The list and chat endpoints are authenticated. Call
prepareSession(once) beforefetchAllEnterprisesoropenChat, or those calls will fail with an auth error. - Channel switch resets the session. Passing a different
hashcodethan last time clears the previous channel's stored token โ intentionally, so you never send a stale token to the new channel. - Session persists. Once
prepareSessionsucceeds the token is stored (encrypted on Android, Keychain on iOS); you don't need to call it on every screen โ only after a fresh install, logout, or channel change. - Already using the full SDK? If the user has already signed in through the SDK's normal launch on this channel, the session exists and you can skip
prepareSessionand go straight to listing.
๐ฉบ Troubleshooting
| Symptom | Likely cause โ fix |
|---|---|
| Empty list, no error | No session yet โ call prepareSession first and start listing in its onReady / after await. Also confirm the hashcode is the one VIH issued for this channel. |
| Auth / 401 error on list or chat | Session missing or stale โ run prepareSession again for the current hashcode (a channel switch clears the old token). |
| Chat opens but history/sending fails | Same as above โ the chat screen needs the authenticated session. Ensure prepareSession completed before openChat. |
| Row title/logo blank | Some channels leave a field null; name falls back through display name โ company name, and logoUrl may be null (show a placeholder). Inspect raw for other fields. |
๐ฎ Support
Questions on the custom Discover flow, a field you need on VihEnterprise, or session/auth behavior? Reach the VIH team with your channel hashcode, SDK version, and the call you're making.