Eloquent

Documentation

@elqnt/chat

Platform-agnostic chat SDK for building conversational interfaces. Works with React (web) and React Native (mobile). Provides real-time messaging via SSE, chat history management, and monitoring capabilities.

Installation

npm install @elqnt/chat

Entry Points

ImportDescription
@elqnt/chatAll exports
@elqnt/chat/hooksReact hooks (primary interface)
@elqnt/chat/modelsTypeScript types
@elqnt/chat/transportTransport layer (SSE, fetch-SSE)
@elqnt/chat/apiInternal API functions (use hooks instead)

React Hooks

useChat

Primary hook for real-time chat functionality with SSE transport support.

import { useChat } from "@elqnt/chat/hooks";

function ChatComponent() {
  const {
    // Connection
    connect,
    disconnect,
    connectionState,
    isConnected,
    // Chat operations
    startChat,
    loadChat,
    sendMessage,
    sendEvent,
    endChat,
    // Typing indicators
    startTyping,
    stopTyping,
    // State
    currentChat,
    chatKey,
    messages,
    error,
    metrics,
    // Events
    on,
    clearError,
  } = useChat({
    baseUrl: "https://api.example.com/chat",
    orgId: selectedOrgId,
    userId: user.id,
    clientType: "customer", // "customer" | "humanAgent" | "observer"
    autoConnect: true,
    debug: false,
  });

  // Connect to SSE server
  useEffect(() => {
    connect();
    return () => disconnect();
  }, []);

  // Start a new chat
  const handleNewChat = async () => {
    const chatKey = await startChat({ source: "website" });
  };

  // Load existing chat
  const handleLoadChat = async (key: string) => {
    const chat = await loadChat(key);
  };

  // Send message
  const handleSend = async (content: string) => {
    await sendMessage(content, attachments);
  };

  // Subscribe to specific events
  useEffect(() => {
    const unsubscribe = on("typing", (event) => {
      console.log("User typing:", event);
    });
    return unsubscribe;
  }, [on]);

  return (
    <div>
      {messages.map((msg) => (
        <Message key={msg.id} {...msg} />
      ))}
      {isConnected && <MessageInput onSend={handleSend} onTyping={startTyping} />}
    </div>
  );
}

Options:

OptionTypeDescription
baseUrlstringChat server base URL
orgIdstringOrganization ID
userIdstringUser ID
clientType"customer" | "humanAgent" | "observer"Client type for routing
transportChatTransport | "sse" | "sse-fetch"Transport type
onMessage(event: ChatEvent) => voidCallback for all events
onError(error: TransportError) => voidCallback for errors
onConnectionChange(state: TransportState) => voidConnection state callback
autoConnectbooleanAuto-connect on mount
retryConfigRetryConfigRetry configuration
debugbooleanEnable debug logging

Methods:

MethodParametersReturnsDescription
connect-Promise<void>Connect to chat server
disconnect-voidDisconnect from server
startChatmetadata?: Record<string, unknown>Promise<string>Start new chat, returns chatKey
loadChatchatKey: stringPromise<Chat>Load existing chat
sendMessagecontent: string, attachments?: unknown[]Promise<void>Send a message
sendEventevent: Omit<ChatEvent, "timestamp">Promise<void>Send custom event
endChatreason?: stringPromise<void>End current chat
startTyping-voidSignal user is typing
stopTyping-voidSignal user stopped typing
oneventType: string, handler: FunctionUnsubscribeSubscribe to event type
clearError-voidClear current error

State:

PropertyTypeDescription
connectionStateTransportStateCurrent connection state
isConnectedbooleanWhether connected
currentChatChat | nullCurrent chat object
chatKeystring | nullCurrent chat key
messagesChatMessage[]Chat messages
errorTransportError | nullCurrent error
metricsConnectionMetricsConnection metrics

useChatHistory

Hook for chat history CRUD operations with loading/error states.

import { useChatHistory } from "@elqnt/chat/hooks";

const {
  loading,
  error,
  getChatHistory,
  getChat,
  updateChat,
  deleteChat,
  getChatsByUser,
} = useChatHistory({
  baseUrl: apiGatewayUrl,
  orgId: selectedOrgId,
});

// Get paginated chat history
const { chats, total, hasMore } = await getChatHistory({ limit: 20, offset: 0 });

// Get single chat
const chat = await getChat("chat-key");

// Update chat (title, pinned)
const updated = await updateChat("chat-key", { title: "New Title", pinned: true });

// Delete chat
const success = await deleteChat("chat-key");

// Get chats by user email
const userChats = await getChatsByUser("user@example.com");

Methods:

MethodParametersReturnsDescription
getChatHistory{ limit?, offset?, skipCache? }{ chats, total, hasMore }Get paginated chat history
getChatchatKey: stringChatSummary | nullGet single chat
updateChatchatKey: string, { title?, pinned? }booleanUpdate chat metadata
deleteChatchatKey: stringbooleanDelete a chat
getChatsByUseruserEmail: stringChatSummary[]Get chats by user

useChatMonitoring

Hook for monitoring active/waiting chats and queues (helpdesk use case).

import { useChatMonitoring } from "@elqnt/chat/hooks";

const {
  loading,
  error,
  getActiveChats,
  getActiveChatsCount,
  getWaitingChatsCount,
  listQueues,
} = useChatMonitoring({
  baseUrl: apiGatewayUrl,
  orgId: selectedOrgId,
});

// Get active chats (last N hours)
const activeChats = await getActiveChats(24); // last 24 hours

// Get counts
const activeCount = await getActiveChatsCount();
const waitingCount = await getWaitingChatsCount();

// List all queues with stats
const queues = await listQueues();
// Returns: [{ id, name, agentId, waitingCount, activeCount }, ...]

Methods:

MethodParametersReturnsDescription
getActiveChatspastHours?: numberChatSummary[]Get active chats
getActiveChatsCount-numberCount of active chats
getWaitingChatsCount-numberCount of waiting chats
listQueues-Queue[]List all queues with stats

useHumanAgentSessions

Hook for monitoring human agent sessions.

import { useHumanAgentSessions } from "@elqnt/chat/hooks";

const {
  loading,
  error,
  getOnlineSessions,
  getAgentSession,
} = useHumanAgentSessions({
  baseUrl: apiGatewayUrl,
  orgId: selectedOrgId,
});

// Get all online agent sessions
const sessions = await getOnlineSessions();

// Get specific agent session
const session = await getAgentSession("agent-uuid");

Methods:

MethodParametersReturnsDescription
getOnlineSessions-AgentSession[]Get all online agent sessions
getAgentSessionagentId: stringAgentSession | nullGet specific agent session

Transport Layer

The chat SDK uses a transport abstraction for real-time communication.

SSE Transport (Browser)

import { createSSETransport } from "@elqnt/chat/transport";

const transport = createSSETransport({ debug: true });

const chat = useChat({
  baseUrl: "...",
  orgId: "...",
  userId: "...",
  transport,
});

Fetch SSE Transport (React Native)

import { createFetchSSETransport } from "@elqnt/chat/transport";

// For React Native where EventSource isn't available
const transport = createFetchSSETransport();

const chat = useChat({
  baseUrl: "...",
  orgId: "...",
  userId: "...",
  transport,
});

Custom Transport

Implement the ChatTransport interface for custom integrations:

import type { ChatTransport, TransportConfig } from "@elqnt/chat/transport";

const customTransport: ChatTransport = {
  connect: async (config: TransportConfig) => { /* ... */ },
  disconnect: (intentional?: boolean) => { /* ... */ },
  send: async (event: ChatEvent) => { /* ... */ },
  sendMessage: async (message: ChatMessage) => { /* ... */ },
  createChat: async (options) => { /* ... */ },
  loadChatData: async (options) => { /* ... */ },
  onMessage: (handler) => { /* ... */ },
  on: (eventType, handler) => { /* ... */ },
  getState: () => "connected",
  getMetrics: () => ({ /* ... */ }),
  getError: () => undefined,
  clearError: () => { /* ... */ },
};

Stream API (Low-level)

Low-level functions for direct HTTP calls (used internally by transport).

import {
  createChat,
  sendChatMessage,
  loadChat,
  endChat,
  sendTypingIndicator,
  sendEvent,
} from "@elqnt/chat/api";

// Create a new chat
const chatKey = await createChat({
  baseUrl: "https://api.example.com/chat",
  orgId: "org-123",
  userId: "user-456",
  metadata: { source: "website" },
});

// Send a message
await sendChatMessage({
  baseUrl: "https://api.example.com/chat",
  orgId: "org-123",
  chatKey,
  userId: "user-456",
  content: "Hello!",
  attachments: [],
});

// Load existing chat
const chat = await loadChat({
  baseUrl: "https://api.example.com/chat",
  orgId: "org-123",
  chatKey: "chat-789",
  userId: "user-456",
});

// Send typing indicator
await sendTypingIndicator({
  baseUrl: "https://api.example.com/chat",
  orgId: "org-123",
  chatKey,
  userId: "user-456",
  typing: true,
});

// End chat
await endChat({
  baseUrl: "https://api.example.com/chat",
  orgId: "org-123",
  chatKey,
  userId: "user-456",
  reason: "User closed chat",
});

Models

Chat

import type { Chat, ChatMessage, ChatUser } from "@elqnt/chat/models";

interface Chat {
  orgId: string;
  key: string;
  title: string;
  messages: ChatMessage[];
  lastUpdated: number;
  startTime: number;
  users: ChatUser[];
  status: ChatStatusTS;
  aiEngaged: boolean;
  humanAgentEngaged: boolean;
  isWaiting: boolean;
  isWaitingForAgent: boolean;
  userRating?: number;
  metadata?: Record<string, unknown>;
  grading?: ChatGrading;
  flow?: ChatFlow;
  context?: ChatContext;
  pinned?: boolean;
  chatType?: ChatTypeTS;
}

interface ChatMessage {
  id: string;
  role: ChatRoleTS;
  content: string;
  time: number;
  status?: string;
  senderId?: string;
  createdAt: number;
  attachments?: ChatAttachment[];
  toolCalls?: ToolCall[];
  metadata?: Record<string, unknown>;
}

interface ChatUser {
  id: string;
  role: ChatRoleTS;
  name: string;
  email: string;
  phone?: string;
  authProvider: string;
  metadata?: Record<string, unknown>;
}

Chat Event

import type { ChatEvent } from "@elqnt/chat/models";

interface ChatEvent {
  type: string;
  orgId: string;
  chatKey?: string;
  userId?: string;
  timestamp: number;
  message?: ChatMessage;
  data?: Record<string, unknown>;
}

// Event types
ChatEventTypeMessage;        // "message"
ChatEventTypeTyping;         // "typing"
ChatEventTypeStoppedTyping;  // "stopped_typing"
ChatEventTypeUserJoined;     // "user_joined"
ChatEventTypeUserLeft;       // "user_left"
ChatEventTypeLoadChat;       // "load_chat"
ChatEventTypeLoadChatResponse; // "load_chat_response"
ChatEventTypeChatEnded;      // "chat_ended"
ChatEventTypeError;          // "error"

Agent Session

import type { AgentSession } from "@elqnt/chat/models";

interface AgentSession {
  agentId: string;
  orgId: string;
  onlineSince: number;
  lastActivity: number;
  status: AgentStatusTS; // "online" | "away" | "busy" | "offline"
  chatsHandled: number;
  activeChats: string[];
  userAgent?: string;
  ipAddress?: string;
  metadata?: Record<string, unknown>;
}

Queue

interface Queue {
  id: string;
  name: string;
  agentId: string;
  waitingCount: number;
  activeCount: number;
}

Transport Types

import type {
  TransportState,
  TransportError,
  TransportConfig,
  RetryConfig,
  ConnectionMetrics,
  ChatTransport,
} from "@elqnt/chat/transport";

type TransportState = "disconnected" | "connecting" | "connected" | "reconnecting";

interface TransportError {
  code: "CONNECTION_FAILED" | "PARSE_ERROR" | "SEND_FAILED" | "TIMEOUT" | "NETWORK_ERROR" | "AUTH_FAILED";
  message: string;
  retryable: boolean;
  timestamp: number;
}

interface ConnectionMetrics {
  latency: number;
  messagesSent: number;
  messagesReceived: number;
  messagesQueued: number;
  reconnectCount: number;
  connectedAt?: number;
  lastMessageAt?: number;
}

interface RetryConfig {
  maxRetries?: number;
  intervals?: number[];
  backoffMultiplier?: number;
  maxBackoffTime?: number;
}

Status Constants

import {
  ChatStatusActive,
  ChatStatusDisconnected,
  ChatStatusAbandoned,
  ChatStatusClosed,
  ChatStatusArchived,
  ChatRoleUser,
  ChatRoleAI,
  ChatRoleHumanAgent,
  ChatRoleSystem,
  ChatRoleTool,
  AgentStatusOnline,
  AgentStatusAway,
  AgentStatusBusy,
  AgentStatusOffline,
} from "@elqnt/chat/models";

API Gateway Routes

FunctionMethodGateway Route
Chat History
ListPOST/api/v1/chats
GetGET/api/v1/chats/{chatKey}
UpdatePATCH/api/v1/chats/{chatKey}
DeleteDELETE/api/v1/chats/{chatKey}
Get by UserGET/api/v1/chats/user/{userEmail}
Active Chats
ListGET/api/v1/chats/active
CountGET/api/v1/chats/active/count
Waiting Chats
CountGET/api/v1/chats/waiting/count
Queues
ListGET/api/v1/queues
Agent Sessions
OnlineGET/api/v1/agents/sessions/online
GetGET/api/v1/agents/sessions/{agentId}
Project Chats
Add to ProjectPOST/api/v1/projects/{projectId}/chats
Update TitlePUT/api/v1/projects/{projectId}/chats/{chatKey}/title
Stream Endpoints
SSE ConnectGET/chat/events
CreatePOST/chat/create
SendPOST/chat/send
LoadPOST/chat/load
EndPOST/chat/end
TypingPOST/chat/typing
EventPOST/chat/event

React Native Support

The SDK works with React Native using fetch-based SSE transport:

import { useChat } from "@elqnt/chat/hooks";
import { createFetchSSETransport } from "@elqnt/chat/transport";

function ChatScreen() {
  const transport = createFetchSSETransport();

  const { messages, sendMessage, isConnected } = useChat({
    baseUrl: config.chatUrl,
    orgId: config.orgId,
    userId: user.id,
    transport,
  });

  return (
    <FlatList
      data={messages}
      renderItem={({ item }) => <MessageBubble message={item} />}
      keyExtractor={(item) => item.id}
    />
  );
}

Type Generation

Chat types are generated from Go using tygo:

cd backend && tygo generate
Go PackageTypeScript Output
blazi/common/chat@elqnt/chat/models/chat-models.ts

See Also