> ## Documentation Index
> Fetch the complete documentation index at: https://docs.callkaro.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Install In Your App

> One URL, and the wrapper for iOS, Android, React Native, Flutter or the web

A chat widget is **one URL**. Everything below is a wrapper around it.

```
https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID
```

Nothing to install, no SDK, no API key, and no server of your own. The page is
hosted by CallKaro, so changing the flow never needs an app release.

<Note>
  The same snippets are on the widget itself in the dashboard — **Widgets → your
  chat widget → Integration** — already filled in with your widget id.
</Note>

***

## Telling Us Who The Visitor Is

Two optional parameters, and both are worth adding if your app knows the answer.

| Parameter | Example                   | What it does                                                                                                                                   |
| --------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `uid`     | `&uid=user_8271`          | Ties the conversation to **the person**. They resume on a new device or after a reinstall, and their conversations group onto one CRM contact. |
| `vars`    | `&vars={"plan":"Growth"}` | Seeds flow variables, so a card can say `{{plan}}` without asking. URL-encode the JSON.                                                        |

Your `uid` is stored **exactly as you send it**. If it happens to be a phone
number it is normalised to digits, so the widget conversation lands on the same
CRM contact as that person's calls and WhatsApp — see
[Variables and identity](/widget/chat/variables#how-the-contact-key-is-chosen).

```
https://widget.callkaro.ai/chat.html?w=wgt_abc123&uid=user_8271&vars=%7B%22plan%22%3A%22Growth%22%7D
```

Without a `uid` the visitor still gets a stable identity of their own, minted in
the browser and kept — so an anonymous visitor is still the same person next
week, on that device.

<Warning>
  `uid` is an **unauthenticated claim**. Anyone who can open the widget can put any
  value in the URL — including a phone number that is not theirs, and be handed
  that contact's CRM record. It is right for a widget embedded behind your own
  app's login, which is what it is for; it must not gate anything the person
  should not see, and it must not be a value that is itself a secret. Signed
  tokens are not supported yet.
</Warning>

***

## The Snippets

<Tabs>
  <Tab title="iOS">
    ```swift theme={null}
    import SafariServices

    // Opens the chat over your current screen and returns the user
    // to it on dismiss. Nothing to add to Info.plist.
    let url = URL(string: "https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID")!
    present(SFSafariViewController(url: url), animated: true)
    ```

    `SFSafariViewController` keeps the user inside your app and needs no extra
    entitlements. Use `WKWebView` instead if you want the chat inside your own
    navigation stack.
  </Tab>

  <Tab title="Android">
    ```kotlin theme={null}
    import android.net.Uri
    import androidx.browser.customtabs.CustomTabsIntent

    CustomTabsIntent.Builder()
        .build()
        .launchUrl(context, Uri.parse("https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID"))
    ```

    Add `androidx.browser:browser` to your Gradle dependencies. Custom Tabs runs
    in the user's own browser, so storage works with no setup — prefer it unless
    the chat has to sit inside your own navigation.

    **If you embed it in a `WebView` instead**, there is one trap:

    ```kotlin theme={null}
    val webView = WebView(this)

    webView.settings.apply {
        javaScriptEnabled = true
        // OFF by default. Without it the chat cannot remember the
        // conversation, so every open starts again from the greeting.
        domStorageEnabled = true
    }

    // Deeplink buttons in your flow arrive here. Handle your own
    // schemes and let everything on our domain stay in the page.
    webView.webViewClient = object : WebViewClient() {
        override fun shouldOverrideUrlLoading(
            view: WebView, request: WebResourceRequest
        ): Boolean {
            if (request.url.host == "widget.callkaro.ai") return false
            startActivity(Intent(Intent.ACTION_VIEW, request.url))
            return true
        }
    }

    webView.loadUrl("https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID")
    ```

    <Warning>
      `domStorageEnabled` is off by default in `WebView` and the failure is
      **silent** — the chat still works, it just forgets the conversation every
      time it opens, which looks like a bug in your flow rather than in the host
      app.
    </Warning>

    To make the back button walk the chat back instead of closing the screen,
    override `onBackPressed()` and call `webView.goBack()` while
    `webView.canGoBack()`.
  </Tab>

  <Tab title="React Native">
    ```jsx theme={null}
    import { WebView } from 'react-native-webview';

    // Rendered as its own screen, so the chat gets the full viewport.
    export function SupportChatScreen() {
      return <WebView source={{ uri: 'https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID' }} />;
    }
    ```

    Or skip the dependency entirely — `Linking.openURL(url)` hands it to the
    system browser.
  </Tab>

  <Tab title="Flutter">
    ```dart theme={null}
    import 'package:webview_flutter/webview_flutter.dart';

    final controller = WebViewController()
      ..loadRequest(Uri.parse('https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID'));

    // then: WebViewWidget(controller: controller)
    ```

    `url_launcher` is the lighter option if you are happy for the chat to open
    outside your app.
  </Tab>

  <Tab title="Website">
    ```html theme={null}
    <!-- Full page -->
    <a href="https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID">Chat with us</a>

    <!-- Or in a panel on your own page -->
    <iframe
      src="https://widget.callkaro.ai/chat.html?w=YOUR_WIDGET_ID"
      style="width:100%;max-width:460px;height:640px;border:0;border-radius:18px"
      title="Chat"
    ></iframe>
    ```

    <Info>
      The floating launcher bubble that the [voice widget](/widget/introduction)
      has is not built for chat yet. This same page in an iframe is the interim.
    </Info>
  </Tab>
</Tabs>

***

## Deep Links Back Into Your App

A button in your flow can open a screen in your own app instead of moving to
another card — "Open Edit Profile", "Show my orders".

Set the button's link to your own scheme, e.g. `myapp://profile/edit`. In a
Custom Tab or `SFSafariViewController` the system routes it to your app. In a
`WebView` you handle it yourself, as in the Android snippet above.

`javascript:`, `data:`, `vbscript:`, `file:` and `blob:` links are rejected when
the flow is saved.

***

## Attachments And The Microphone

On an **AI assistant** card, and during a **human handoff**, the composer offers
a paperclip and a microphone. Two things that catch people out inside an app:

* A `WebView` must grant the page's own microphone permission request, on top of
  the host app's OS-level permission. A Custom Tab or
  `SFSafariViewController` handles this for you.
* Recording needs a secure context, which every URL above already is.

Size limits are on the
[introduction page](/widget/chat/introduction#limits-worth-designing-around).

***

## Checking It

Open the URL in a desktop browser first. If the flow runs there, the embed will
run — every snippet above loads the same page.

If it does not, work down
[Troubleshooting](/widget/chat/troubleshooting) — the two usual causes are an
agent that was never **published** and an inactive **Widgets subscription**.

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Card types" icon="diagram-project" href="/widget/chat/card-types">
    What you can build into the flow
  </Card>

  <Card title="Variables and identity" icon="brackets-curly" href="/widget/chat/variables">
    `uid`, `vars`, and how contacts are keyed
  </Card>
</CardGroup>
