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

# Flutter chat for logged-in users

> Connect the Flutter chat to your signed-in customers through your backend

Use server mode when the chat must follow a signed-in customer across devices, including between your app and your website. The app never receives your account-wide API-Token: your backend authenticates the customer, finds or creates their chat session, and returns a short-lived stream token for that one session.

Server mode works with **Anonymous usage** switched off and is not subject to the anonymous limits. See [Security and limits](https://docs.osvi.ai/website-chat/security.md#logged-in-users-server-mode).

## Your backend

The backend is the same one the website chat uses. Follow [Logged-in users](https://docs.osvi.ai/website-chat/server-mode.md#session-flow) for the session flow and the Node and Python sketches. Your token endpoint returns:

```json theme={null}
{ "sessionId": "…", "token": "…", "wsUrl": "wss://…", "expiresAt": 1767225600 }
```

When the app asks for a new session, your backend must create a fresh one instead of reusing the active session.

## The app

Pass `ChatAuth.server` with a `getToken` callback that calls your token endpoint with your app's own authentication:

```dart theme={null}
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:osvi_chat/osvi_chat.dart';

final chat = await OsviChatClient.create(
  agentId: 'agent_yourAgentId',
  auth: ChatAuth.server(getToken: ({bool newSession = false}) async {
    final response = await http.post(
      Uri.parse('https://api.example.com/chat/token'),
      headers: {
        'Authorization': 'Bearer ${await myAuth.accessToken()}',
        'Content-Type': 'application/json',
      },
      body: jsonEncode({'newSession': newSession}),
    );
    if (response.statusCode != 200) throw Exception('Unable to start chat');
    return ServerToken.fromJson(jsonDecode(response.body) as Map<String, Object?>);
  }),
);
```

`ServerToken.fromJson` accepts `sessionId`, `token`, `wsUrl` and `expiresAt`, or the snake\_case names `session_id`, `ws_url` and `expires_at`, so your endpoint can pass the Osvi AI response through. `expiresAt` may be in seconds or milliseconds.

The SDK calls `getToken` when the chat starts, about every 4.5 minutes while it is connected, after the server rejects a token, and with `newSession: true` when the visitor taps **New chat**. If `getToken` throws, `state.error` has the code `token_failed` and the chat shows **Try again**.

`wsUrl` must use `wss://`. The client accepts `ws://` only for `localhost` and `127.0.0.1`, for local development; anything else fails with `token_failed`.

## Identity

A server-mode client belongs to one customer. When the customer logs out or switches accounts, call `dispose()` on the client and create a new one for the next customer. In server mode the client stores no visitor ID and no session ID on the device.

Your token endpoint is your security boundary: authenticate the user, take the customer ID from your own session rather than from the request, and rate-limit the endpoint. Do not use the visitor format (`web:` followed by 16 to 64 URL-safe characters) as a customer ID; Osvi AI rejects it with `422 invalid_customer_id`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.