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

> ## Agent Instructions
> Internal links on these pages omit the .md extension. Append .md to a docs page URL, or send an Accept: text/markdown header, to get that page as markdown.

# chat

***

## Overview

Chat module for following the AI chat of your users' apps in real time. Call
[`init()`](#init) to create a session and start following.

A session holds one socket and up to eight app subscriptions. For each app, you get a
snapshot of the app's chat, then every change as it happens:

* New and updated chat messages, with the AI's tool calls.
* The AI's status, from processing to ready.
* Preview reloads and navigation, the prompt queue, and generated images.

## Methods

### init()

> **init**(`options`): `ChatSession`

Creates a session without connecting it. A [session](#session-methods) is one socket plus the
app subscriptions that share it.

Pass a `getSession()` callback in the options. The client calls it to get a
[socket session](/developers/references/apps-api/sections/socket-sessions), which your server
opens with [Create socket session](/api-reference/create-socket-session) and returns as the
response body. Each session is independent and gets its own socket session.

Call [`connect()`](#connect) on the returned session to open the socket.

#### Parameters

<ParamField body="options" type="ChatInitOptions" required>
  Session options.
</ParamField>

<Accordion title="Properties">
  <ParamField body="getSession" type="() => Promise<SocketSession>" required>
    Returns a new socket session from your server.

    Return the response body of [Create socket session](/api-reference/create-socket-session) as a
    [`SocketSession`](#socketsession), and return a new session on every call. The client calls
    `getSession()` on the first connect(), and again only when
    the session expires or Base44 rejects its token. If a new session has a different `socket_url`
    or `socket_path`, the client moves to it and rejoins every active subscription.
  </ParamField>

  <ParamField body="onError" type="(error: PlatformSocketError) => void" required>
    Called on errors that affect the whole session, such as a failed connection or an ended session.
    Errors for one app go to that subscription's `onError()` instead. The
    [error codes](#errors)
    say what each error means.
  </ParamField>
</Accordion>

#### Returns

[`ChatSession`](#session-methods)

The new session.

#### Example

<CodeGroup>
  ```typescript Create and connect a session theme={null}
  const chat = client.chat.init({
    async getSession() {
      const response = await fetch('/api/chat-socket-session', { method: 'POST' });
      return response.json();
    },
    onError(error) {
      console.error('Live updates error', error.code);
    },
  });
  await chat.connect();
  ```
</CodeGroup>

## Session methods

A session is one socket plus the app subscriptions that share it, and it reconnects automatically
if the connection drops. Use the methods below to connect the socket, follow apps, and close the
session.

### connect()

> **connect**(): `Promise`\<`void`>

Connects the session's socket.

The first call gets a socket session from [`getSession()`](#init). If the session has expired
or Base44 rejects its token, the client calls `getSession()` once more for a new one.
Concurrent calls share one attempt.

The promise resolves once the socket connects, before any snapshot arrives. If the connection
drops, the session retries five times and rejoins every active subscription, each with a fresh
snapshot.

Call `connect()` again after a failure or a `session_replaced` error.

#### Returns

`Promise<void>`

A promise that resolves when the socket connects.

#### Throws

The promise rejects with a `PlatformSocketError` whose code is `connection_denied`, `connection_failed`, or `session_unavailable` when the session can't connect, or `client_closed` when the session is closed first.

#### Example

<CodeGroup>
  ```typescript Connect and handle failure theme={null}
  try {
    await chat.connect();
  } catch (error) {
    console.error('Could not connect', error.code);
  }
  ```
</CodeGroup>

***

### subscribe()

> **subscribe**(`appId`, `options`): `PlatformSubscription`

Subscribes to an app's chat.

You can subscribe before or after connecting. A session has one subscription per app, up to
eight in total. A session can only follow apps your server gave the session access to, either
in [Create socket session](/api-reference/create-socket-session) or later with
[Add app to session](/api-reference/add-app-to-session). Subscribing to any other app fails
with `access_denied`. The subscription first receives a snapshot of the app, then each live
event.

Pass `onSnapshot()`, `onEvent()`, and `onError()` callbacks in the options to receive the app's
snapshot, live events, and errors.

Callbacks for an app run one at a time, in order. If a callback returns a promise, the session
waits for it before calling the next callback for that app. Each app has its own queue, so a
slow callback for one app doesn't delay the others. If more than 1,000 snapshots and events are
waiting for an app's callbacks, the subscription ends with `delivery_overflow`.

#### Parameters

<Accordion title="Properties">
  <ParamField body="appId" type="string" required>
    ID of the app to follow, 24 lowercase hexadecimal characters.
  </ParamField>

  <ParamField body="options" type="SubscriptionOptions" required>
    Callbacks for the app's snapshots, events, and errors.
  </ParamField>

  <Accordion title="Properties">
    <ParamField body="onSnapshot" type="(snapshot: PlatformSnapshot) => void | Promise<void>" required>
      Called with the app's current state, a [`PlatformSnapshot`](#platformsnapshot). Replace everything you hold for the app with the snapshot.

      The snapshot arrives when you subscribe, after every reconnect, and after the main conversation
      is rewritten by an undo, restore, or sync. Live events follow it. A snapshot holds only the last
      50 messages, so load older history again if you show it.
    </ParamField>

    <ParamField body="onEvent" type="(event: PlatformEvent) => void | Promise<void>" required>
      Called with each live event, a [`PlatformEvent`](#platformevent). Events for an app arrive one at a time, in order. If the callback
      throws or rejects, the subscription ends with `handler_failed`.

      New event types can be added at any time, so ignore the types you don't handle. An event newer
      than this SDK arrives with `type: "unknown"`, and its event name is in `name`.
      [Socket messages](/developers/references/apps-api/sections/socket-messages) lists every event
      and its payload.
    </ParamField>

    <ParamField body="onError" type="(error: PlatformSocketError) => void" required>
      Called on errors for this app. Every error ends the subscription except `snapshot_unavailable`.
      The [error codes](#errors)
      say what each error means.
    </ParamField>
  </Accordion>
</Accordion>

#### Returns

`PlatformSubscription`

A subscription to an app's chat.

<Accordion title="Properties">
  <ResponseField name="appId" type="string" required>
    ID of the app.
  </ResponseField>

  <ResponseField name="active" type="boolean" required>
    Whether the subscription still receives updates. The value becomes `false` after an error or a call to unsubscribe().
  </ResponseField>
</Accordion>

#### Throws

* The method throws a `TypeError` when `appId` isn't a valid app ID or the app already has a subscription.
* The method throws a `PlatformSocketError` whose code is `subscription_limit` when the session already has eight subscriptions.

#### Example

<CodeGroup>
  ```typescript Follow an app theme={null}
  const subscription = chat.subscribe(appId, {
    onSnapshot(snapshot) {
      renderChat(snapshot.messages);
    },
    onEvent(event) {
      if (event.type === 'message.updated') upsertMessage(event.data.message);
    },
    onError(error) {
      console.error(`App ${error.appId}`, error.code);
    },
  });
  ```
</CodeGroup>

***

### close()

> **close**(): `void`

Closes the session.

Call `close()` when the page closes or you no longer need live updates. Closing ends every
subscription, stops reconnecting, and disconnects the socket. Calling `close()` again does
nothing. A closed session can't be reused, so call [`init()`](/developers/references/platform-sdk/docs/interfaces/chat#init)
for a new one.

#### Returns

`void`

#### Example

<CodeGroup>
  ```typescript Close a session theme={null}
  chat.close();
  ```
</CodeGroup>

## Type Definitions

### SocketSession

The response body of [Create socket session](/api-reference/create-socket-session), which your
`getSession()` callback returns. Pass the response through unchanged. The client reads the three
fields below.

<ResponseField name="session_token" type="string" required>
  Token that authenticates the socket. The client keeps it in memory and sends it only when the socket connects.
</ResponseField>

<ResponseField name="socket_url" type="string" required>
  Origin of the platform socket, with no path, query, fragment, or credentials.
</ResponseField>

<ResponseField name="socket_path" type="string" required>
  Absolute path the socket connects on, such as `/ws/socket.io/`.
</ResponseField>

### PlatformEvent

> **PlatformEvent** = `AppEvent`\<`"message.updated"`> | `AppEvent`\<`"message.removed"`> | `AppEvent`\<`"app.status_changed"`> | `AppEvent`\<`"preview.reload_requested"`> | `AppEvent`\<`"preview.navigation_requested"`> | `AppEvent`\<`"queue.updated"`> | `AppEvent`\<`"task.progressed"`> | `AppEvent`\<`"image.resolved"`> | `AppEvent`\<`"conversation.changed"`> | `AppEvent`\<`"files.changed"`> | `AppEvent`\<`"branch.deleted"`> | `AppEvent`\<`"repository.changed"`> | `AppEvent`\<`"pull_request.changed"`> | `UnknownEvent`

A live event for a subscribed app.

Check `type` to narrow `data` to that event's payload. The `data` of an event holds only the
fields that changed. Keep your value for any field the event leaves out, and clear a field the
event sets to `null`.
[Socket messages](/developers/references/apps-api/sections/socket-messages) lists every event
and its payload.

An event added to the service after this version of the SDK arrives with `type: "unknown"`, its
event name in `name`, and an unchecked `data`, so check a field before using it. After you
upgrade, the event arrives under its own `type`.

#### Example

<CodeGroup>
  ```typescript Narrow an event by type theme={null}
  function onEvent(event: PlatformEvent) {
    if (event.type === "message.updated") {
      const { message } = event.data;
      console.log(`Message ${message.id} changed`);
    }
    // A hypothetical event this version of the SDK doesn't know yet
    if (event.type === "unknown" && event.name === "app.renamed" && typeof event.data.name === "string") {
      console.log(`App renamed to ${event.data.name}`);
    }
  }
  ```
</CodeGroup>

### PlatformSnapshot

An app's current state.

The snapshot arrives when you subscribe, after every reconnect, and after the main conversation
is rewritten. Replace everything you hold for the app with the snapshot. A snapshot holds only
the last 50 messages.

<ResponseField name="status" type="StatusObject | null" required>
  The app's build status, or `null` when none is recorded.
</ResponseField>

<ResponseField name="messages" type="Message[]" required>
  The last 50 messages visible to partners, oldest first.
</ResponseField>

<ResponseField name="room" type="string" required>
  Identifies the app the snapshot is for, in the form `/apps/{app_id}`.
</ResponseField>

<ResponseField name="queue" type="QueueState">
  The main branch's prompt queue. Missing when the server sends none.
</ResponseField>

## Errors

### PlatformSocketError

The error the SDK reports when something goes wrong with live updates.

You don't create these errors. The SDK passes them to your `onError()` callbacks, rejects
`connect()` with them, and throws them from `subscribe()`. Check `code` to see what happened,
then look the code up below:

* Session error codes go to the `onError()` you pass to `init()`. The whole
  session is affected, and `appId` isn't set.
* App error codes go to the subscription's `onError()`. Only that app is
  affected, and `appId` says which one.

These errors never carry the session token, a callback's exception, or the server's exception
text.

#### Example

<CodeGroup>
  ```typescript Handle session and app errors theme={null}
  const chat = client.chat.init({
    async getSession() {
      const response = await fetch('/api/chat-socket-session', { method: 'POST' });
      return response.json();
    },
    // Session error codes: the whole session is affected
    onError(error) {
      if (error.code === 'session_revoked') showSignedOut();
    },
  });

  chat.subscribe(appId, {
    onSnapshot: renderChat,
    onEvent: applyEvent,
    // App error codes: only this app is affected, and error.appId says which
    onError(error) {
      if (error.code === 'access_revoked') hideLiveUpdates(error.appId);
    },
  });
  ```
</CodeGroup>

<ResponseField name="code" type="PlatformSocketErrorCode" required>
  What happened. One of the session error codes or app error codes.
</ResponseField>

<Accordion title="Session error codes">
  Codes of errors that affect the whole session. They go to the `onError()` you pass to `init()`,
  and `connect()` rejects with them. A retryable error is one where the same call can succeed again
  without changing anything.

  <ResponseField name="session_unavailable">
    The `getSession()` callback threw, took longer than 20 seconds, or returned a session without a
    valid `session_token`, a `socket_url` that is an HTTP or HTTPS origin, or a `socket_path` that is
    an absolute path.

    Check the endpoint that opens sessions, then call `connect()` again. Retryable. If a field is
    invalid, fix it on your server first.
  </ResponseField>

  <ResponseField name="connection_denied">
    Base44 refused the session even with a fresh token. Either the token is invalid, or live updates
    aren't turned on for the workspace.

    Check that your server opens a new session on every call. Not retryable.
  </ResponseField>

  <ResponseField name="connection_failed">
    The socket couldn't connect after five retries, or the server closed the connection.

    Call `connect()` again, for example when the tab next gets focus. Retryable.
  </ResponseField>

  <ResponseField name="session_revoked">
    Your server or an admin ended the session, or the workspace API key that opened the session lost
    access.

    Open a new session from your server if the page should still follow apps. Not retryable.
  </ResponseField>

  <ResponseField name="session_replaced">
    Another socket connected with the same session token.

    Use one session per tab. Don't reconnect automatically. Call `connect()` only to take the
    session back on purpose. Not retryable.
  </ResponseField>

  <ResponseField name="protocol_error">
    A message about the whole session didn't match the protocol.

    Update the SDK, then call `connect()` again. Not retryable.
  </ResponseField>

  <ResponseField name="client_closed">
    You called `connect()` or `subscribe()` after `close()`.

    Create a new session with `init()`. Not retryable.
  </ResponseField>
</Accordion>

<Accordion title="App error codes">
  Codes of errors for a single app. They go to the subscription's `onError()`, and `subscribe()`
  throws `subscription_limit`. Every one ends the subscription except `snapshot_unavailable`.

  <ResponseField name="access_denied">
    The session doesn't have access to the app, or the connection subscribed to apps too often.

    Add the app to the session from your server, then subscribe again. Not retryable.
  </ResponseField>

  <ResponseField name="access_revoked">
    The session lost access to the app. Your server removed the app from the session, or the app
    left the workspace.

    Stop showing the app's live updates. Not retryable.
  </ResponseField>

  <ResponseField name="snapshot_unavailable">
    The subscription is active, but its snapshot failed. Events keep arriving.

    Load the app's messages from the Apps API instead.
  </ResponseField>

  <ResponseField name="subscription_limit">
    The session already has eight subscriptions.

    Unsubscribe from an app, or open another session. Not retryable.
  </ResponseField>

  <ResponseField name="delivery_overflow">
    More than 1,000 snapshots and events were waiting for your callbacks.

    Make the callbacks faster, then subscribe again for a fresh snapshot. Retryable.
  </ResponseField>

  <ResponseField name="handler_failed">
    Your `onSnapshot()` or `onEvent()` callback threw or rejected.

    Fix the callback, then subscribe again. Not retryable.
  </ResponseField>

  <ResponseField name="protocol_error">
    An event for this app didn't match the protocol.

    Update the SDK, then subscribe again. Not retryable.
  </ResponseField>
</Accordion>

<ResponseField name="appId" type="string">
  ID of the app the error is about. Set for app error codes, and not set for session error codes.
</ResponseField>


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