Skip to main content
A session is a conversation thread with message history. This page covers how sessions are created, loaded, persisted, and managed.

Overview

Session Creation

Sessions are created by the client. The client generates a UUID and sends it as the X-Session-Id header. The message rides in the query string as message.
const sessionId = crypto.randomUUID();

const response = await fetch(
  `/api/chat?message=${encodeURIComponent("Hello")}&app=mycoindex`,
  {
    method: "POST",
    headers: {
      "X-Session-Id": sessionId,
      "AOMI-APP-KEY": "sk-mcd-...",
    },
  },
);
If no session exists for that ID, the backend creates one automatically on the first request.

Session Loading: Three-Tier Strategy

When a request arrives, the backend loads the session using a three-tier approach:
  1. Memory cache: the session is already active in the server’s memory. This is the fastest path and handles the common case where a user is mid-conversation.
  2. Database: the session was previously persisted to PostgreSQL. It is loaded and cached in memory.
  3. Create new: no prior session exists. A fresh session is created and cached.

Message Persistence

Messages are persisted to PostgreSQL as they are exchanged. This ensures conversation history survives server restarts and enables users to resume conversations. Each message record includes:
FieldTypeDescription
idUUIDUnique message identifier
session_idUUIDParent session
thread_idUUIDParent thread
rolestringuser, assistant, or system
contenttextMessage text content
tool_callsJSONTool calls made by the assistant (if any)
tool_resultsJSONResults from tool execution (if any)
created_attimestampWhen the message was created

Wallet Binding

Sessions can optionally be associated with a wallet address (public key). This enables wallet-aware behavior:
  • Tool context: tools like GetPortfolio can automatically scope queries to the connected wallet.
  • Persistent history: conversations tied to a public key persist across sessions and devices.
  • Cross-session continuity: the assistant remembers previous interactions when the same wallet reconnects.
Wallet binding is optional. Sessions work without a wallet for non-Web3 use cases.

Thread Management

Within a session, users can create multiple threads, separate conversation topics with independent message histories.

Operations

OperationAPI CallDescription
CreatePOST /api/sessionsStart a new thread
ListGET /api/sessions?public_key={key}List all threads for a wallet
GetGET /api/sessions/{id}Fetch one thread by ID
SwitchClient-sideChange the active thread (update X-Session-Id)
RenamePATCH /api/sessions/{id}Update a thread’s title
DeleteDELETE /api/sessions/{id}Remove a thread and its messages

Using the Widget

The widget’s sidebar provides a thread management UI out of the box:
  • Click New Thread to start a new conversation.
  • Click a thread in the sidebar to switch to it.
  • Right-click or use the menu to rename or delete threads.

Using the Headless Lib

With @aomi-labs/react, manage threads programmatically:
import { useAomiRuntime, useThreadContext } from "@aomi-labs/react";

function ThreadManager() {
  const { currentThreadId } = useThreadContext();
  const { createThread, deleteThread, selectThread, renameThread } =
    useAomiRuntime();

  return (
    <div>
      <p>Current thread: {currentThreadId}</p>
      <button onClick={() => createThread()}>New Thread</button>
      <button onClick={() => renameThread(currentThreadId, "My Topic")}>
        Rename
      </button>
    </div>
  );
}

Session State

GET /api/state returns a snapshot of the session:
type StateResponse = {
  messages: Message[] | null;
  system_events: SystemEvent[] | null;
  title: string | null;
  is_processing: boolean; // true while the assistant is working
  user_state: {
    connection?: { is_connected?: boolean | null };
    evm?: { address?: string | null; chain_id?: number | string | null };
  } | null;
};
Wallet fields are nested: the address is user_state.evm.address, the chain is user_state.evm.chain_id, and connection status is user_state.connection.is_connected.

Polling State

Pass the session ID in the X-Session-Id header and poll for the current snapshot:
const state = await fetch("/api/state", {
  headers: { "X-Session-Id": sessionId },
}).then((r) => r.json());

console.log(state.is_processing); // true | false
console.log(state.user_state?.evm?.address); // "0x742d..." or undefined

Real Time Updates

For real time updates without polling, subscribe to the SSE updates channel. The session ID travels in a header, so open it with fetch and read the body instead of using EventSource:
const response = await fetch("/api/updates", {
  headers: {
    "X-Session-Id": sessionId,
    Accept: "text/event-stream",
  },
});
// Read response.body and parse each `data:` line as JSON.
// Event types: title_changed, tool_update, tool_complete, system_notice.
See the API Reference for a full consumer example.

Next Steps

Last modified on June 4, 2026