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

# Opening chat

> Connect Sagepilot chat to app-owned buttons, tabs, badges, and help screens.

The SDK does not force a launcher UI. Use the hook to connect Sagepilot to your own button, tab, badge, or help screen.

```tsx theme={null}
import { Pressable, Text } from "react-native";
import { useSagepilotChat } from "@sagepilot-ai/react-native-sdk";

export function SupportButton() {
  const { configured, unreadCount, presentMessages } = useSagepilotChat();

  return (
    <Pressable disabled={!configured} onPress={presentMessages}>
      <Text>
        Support{unreadCount > 0 ? ` (${unreadCount})` : ""}
      </Text>
    </Pressable>
  );
}
```

## Open helpers

```ts theme={null}
import { SagepilotChat } from "@sagepilot-ai/react-native-sdk";

SagepilotChat.present();
SagepilotChat.presentMessages();
await SagepilotChat.presentMessageComposer("I need help with my order");
await SagepilotChat.presentMessageComposer("I need help with my order", { mode: "auto" });
await SagepilotChat.presentMessageComposer("Start a new request", { mode: "new" });
await SagepilotChat.presentMessageComposer("I need help with this order", { chatId: savedChatId });
```

`presentMessageComposer(message)` uses `{ mode: "auto" }` by default. It pre-fills the composer and lets Sagepilot reuse an existing conversation when one is available. If there is no existing conversation, Sagepilot starts a new one when the customer sends the message.

Use `{ mode: "new" }` only when the button or workflow should always start a fresh conversation.

Use `{ chatId }` when an app-owned surface should reopen a specific conversation, such as an order help button.

`presentMessageComposer()` returns `boolean` by default. If `behavior.waitForIdentifyBeforeComposer` is `true` and `identify()` is already in flight, it returns `Promise<boolean>` and waits up to 60 seconds for identity to finish before opening. You can `await` it in both cases.

For authenticated support flows, enable `behavior.waitForIdentifyBeforeComposer` and call `identify()` before opening chat:

```ts theme={null}
const opened = await SagepilotChat.presentMessageComposer("I need help with my order");
```

Subscribe to conversation creation when your app needs to store a newly created chat ID:

```ts theme={null}
await SagepilotChat.presentMessageComposer("I need help with this order", {
  mode: "new",
  metadata: { source: "order_help", orderId },
  onConversationCreated: ({ chat_id, metadata }) => {
    saveChatForOrder(metadata?.orderId, chat_id);
  }
});

const unsubscribe = SagepilotChat.onConversationCreated(({ chat_id }) => {
  cacheCreatedChat(chat_id);
});
```

## Custom launcher example

```tsx theme={null}
import { Pressable, Text, View } from "react-native";
import { MessageCircle } from "lucide-react-native";
import { useSagepilotChat } from "@sagepilot-ai/react-native-sdk";

export function ChatLauncher() {
  const { configured, unreadCount, presentMessages, theme } = useSagepilotChat();
  const launcher = theme.launcher;

  return (
    <Pressable
      accessibilityRole="button"
      accessibilityLabel={launcher.label}
      disabled={!configured}
      onPress={presentMessages}
      style={({ pressed }) => ({
        alignItems: "center",
        backgroundColor: !configured
          ? launcher.disabledButtonColor
          : pressed
            ? launcher.pressedButtonColor
            : launcher.buttonColor,
        borderColor: !configured ? launcher.disabledBorderColor : launcher.borderColor,
        borderRadius: 28,
        borderWidth: 1,
        height: 56,
        justifyContent: "center",
        width: 56
      })}
    >
      <MessageCircle
        color={!configured ? launcher.disabledContentColor : launcher.iconColor}
        fill={!configured ? launcher.disabledContentColor : launcher.iconInsideColor}
        size={24}
      />
      {unreadCount > 0 ? (
        <View
          style={{
            alignItems: "center",
            backgroundColor: launcher.unreadBadgeColor,
            borderColor: launcher.unreadBadgeBorderColor,
            borderRadius: 9,
            borderWidth: 1,
            minWidth: 18,
            paddingHorizontal: 4,
            position: "absolute",
            right: -2,
            top: -2
          }}
        >
          <Text style={{ color: launcher.unreadBadgeTextColor, fontSize: 11 }}>
            {unreadCount}
          </Text>
        </View>
      ) : null}
    </Pressable>
  );
}
```
