Skip to main content

Chat Module

Access via sdk.chat The Chat module enables real-time messaging between your application and Wonderful agents. Sessions are established over WebSocket for low-latency, bidirectional communication.

Starting a Session

The target agent must have Omni-Channel enabled in the Wonderful Platform. This requirement is temporary and will be removed in a future release.

start(params): Promise<ChatSession>

Start a new chat session.
const session = await sdk.chat.start({
  from: 'user-123',             // REQUIRED - sender identifier
  fromType: 'phone',            // REQUIRED - 'phone' | 'email' | 'name'
  agentId: 'agent-uuid',        // Optional - target agent
  to: '+1234567890',            // Optional - recipient identifier
  toType: 'phone',              // Optional - 'phone' | 'email' | 'name'
  locale: 'en-US',              // Optional - defaults to 'en-US'
  metadata: {                   // Optional - custom data attached to the session
    name: 'John Doe',
    gender: 'male',
    platform: 'app',
    customer_id: 'cust-123',
  }
});

// Returns ChatSession with:
// - sessionId: unique identifier (= Communication ID in backend)
// - status: 'in-progress'
// - messages: [] (empty for new session)
// - connectionState: 'connected'
Standard metadata keys:
KeyTypeDescription
namestringUser’s display name
gender'male' | 'female' | 'other'User’s gender
platform'web' | 'app'Client platform type
You can also include any custom key-value pairs in metadata.

Understanding the from Field

The from field is the primary customer identifier displayed in the Wonderful Platform:
  • Activities Tab: Shows as the main identifier for each conversation
  • Communication Record: Stored as the customer’s identity
  • Session Lookup: Used to associate conversations with customers
Best practices:
  • Use a consistent identifier per customer (e.g., user ID, phone, email)
  • The value should be meaningful to your support team
  • Phone numbers should include country code: '+1234567890'

Identity Types

type IdentityType = 'phone' | 'email' | 'name';
TypeWhen to UseStorageExamples
'phone'Customer identified by phoneStored in from_number'+1234567890'
'email'Customer identified by emailStored in from_email'user@example.com'
'name'Custom identifier/display nameDisplay only (not stored)'user-123', 'John Doe'
Use 'phone' or 'email' when you need the identifier stored for filtering/lookup. Use 'name' for custom IDs.

Recovering a Session

recover(sessionId): Promise<ChatSession>

Recover an existing session (e.g., after app restart or page refresh). Loads previous messages.
const session = await sdk.chat.recover('session-uuid');

// Returns ChatSession with:
// - sessionId: the recovered session ID
// - status: 'in-progress' or 'completed' or 'failed'
// - messages: array of previous messages
// - connectionState: 'connected' (if active)

console.log(session.messages); // Previous messages are loaded

if (session.status === 'in-progress') {
  await session.send({ type: 'text', text: "I'm back!" });
}

Recovery Edge Cases

ScenarioBehavior
Session doesn’t existThrows SESSION_NOT_FOUND
Session already completedReturns session with status: 'completed'
Participant data missingUses 'name' as default fromType
Multiple recovery callsSafe — returns same session state
try {
  const session = await sdk.chat.recover('session-123');
  if (session.status === 'completed') {
    // Session already ended, show history only
  }
} catch (error) {
  if (error.code === 'SESSION_NOT_FOUND') {
    // Start new session instead
  }
}

ChatSession

Returned from start() or recover().

Properties

interface ChatSession {
  // Properties
  readonly sessionId: string;      // = Communication ID in backend
  readonly agentId?: string;       // Agent ID (if available)
  readonly status: SessionStatus;
  readonly connectionState: ConnectionState;
  readonly createdAt: string;              // ISO 8601
  readonly participant: SessionParticipant;
  readonly messages: Message[];

  // Methods
  send(message: ChatMessage): Promise<SendResponse>;
  end(): Promise<void>;
  forward(destination: string): Promise<void>;
  onEvent(callback: (event: SessionEvent) => void): () => void;
}

type SessionStatus = 'in-progress' | 'completed' | 'failed';

type ConnectionState = 'connecting' | 'connected' | 'reconnecting' | 'disconnected';

interface SessionParticipant {
  from: string;
  fromType: IdentityType;
  to?: string;
  toType?: IdentityType;
}

Sending Messages

send(message): Promise<SendResponse>

Send a message to the agent. Supports text, images, and documents.
// Text message
const response = await session.send({
  type: 'text',
  text: 'Hello, I need help with my order'
});
// Returns: { eventId: 'uuid', timestamp: '2024-02-02T13:00:00.000Z' }

// Text with custom metadata
await session.send({
  type: 'text',
  text: 'Order #12345',
  metadata: { order_id: '12345', priority: 'high' }
});

// Image (provide URL to hosted image)
await session.send({
  type: 'image',
  mediaUrl: 'https://example.com/screenshot.png',
  metadata: { context: 'error_screenshot' }
});

// Document/File (provide URL to hosted file)
await session.send({
  type: 'document',
  mediaUrl: 'https://example.com/invoice.pdf',
  fileName: 'invoice.pdf',
  metadata: { document_type: 'invoice' }
});

ChatMessage

interface ChatMessage {
  type: 'text' | 'image' | 'document';
  text?: string;
  mediaUrl?: string;
  fileName?: string;
  metadata?: Record<string, unknown>;
}

SendResponse

interface SendResponse {
  eventId: string;
  timestamp: string;  // ISO 8601 format: '2024-02-02T13:00:00.000Z'
}

Validation Rules

  • Text messages: text field is required
  • Image/Document messages: mediaUrl field is required
// Valid
await session.send({ type: 'text', text: 'Hello' });
await session.send({ type: 'image', mediaUrl: 'https://...' });

// Throws INVALID_REQUEST
await session.send({ type: 'text' });  // Missing text
await session.send({ type: 'image' }); // Missing mediaUrl

Session Control

end(): Promise<void>

End the session gracefully.
await session.end();
// Session status changes to 'completed'
// Triggers 'ended' event with endedBy: 'user'

forward(destination): Promise<void>

Forward the session to another destination (team, queue, or agent).
await session.forward('support-team');
// Triggers 'ended' event with reason: 'forwarded'
ParameterTypeDescription
destinationstringTarget identifier (team name, queue ID, or agent ID)
Available destinations depend on your Wonderful Platform configuration. Contact support for setup.

Real-time Events

onEvent(callback): () => void

Subscribe to real-time session events via WebSocket. Returns an unsubscribe function. The SDK maintains a WebSocket connection to receive events in real-time. Connection state events help you handle network issues gracefully.
const unsubscribe = session.onEvent((event) => {
  switch (event.type) {
    // New message received (from agent or system)
    case 'message':
      handleMessage(event.data);
      break;

    // WebSocket connected successfully
    case 'connected':
      console.log('Connected!');
      break;

    // WebSocket disconnected (network issue, server restart, etc.)
    case 'disconnected':
      // willRetry: true means SDK will auto-reconnect
      if (event.data.willRetry) {
        showReconnectingUI();
      } else {
        showConnectionLostUI();
      }
      break;

    // Reconnection attempt in progress
    case 'reconnecting':
      showNotification(
        `Reconnecting... (attempt ${event.data.attempt}/${event.data.maxAttempts})`
      );
      break;

    // WebSocket reconnected after disconnection
    case 'reconnected':
      hideReconnectingUI();
      // missedMessages: number of messages received while disconnected
      break;

    // Error occurred
    case 'error':
      console.error(event.data.message);
      if (event.data.code === 'PII_MASKING_ENABLED') {
        showPiiMaskingWarning();
      }
      break;

    // Session ended (by user, agent, or system)
    case 'ended':
      console.log('Session ended by:', event.data.endedBy);
      navigateAway();
      break;
  }
});

// Later: stop listening and close WebSocket
unsubscribe();

Event Types

type SessionEvent =
  | MessageEvent
  | ConnectedEvent
  | DisconnectedEvent
  | ReconnectingEvent
  | ReconnectedEvent
  | ErrorEvent
  | SessionEndedEvent;
Event TypeDataDescription
messageMessageNew message received
connected{ sessionId, connectedAt }WebSocket connected
disconnected{ reason, willRetry }WebSocket disconnected
reconnecting{ attempt, maxAttempts }Reconnection in progress
reconnected{ reconnectedAt, missedMessages }Successfully reconnected
ended{ reason, endedBy }Session ended
error{ code, message, recoverable }Error occurred

Event Type Definitions

interface MessageEvent {
  type: 'message';
  data: Message;
}

interface ConnectedEvent {
  type: 'connected';
  data: { sessionId: string; connectedAt: string };  // ISO 8601
}

interface DisconnectedEvent {
  type: 'disconnected';
  data: {
    reason: 'network' | 'timeout' | 'server' | 'manual';
    willRetry: boolean;  // true = SDK will auto-reconnect
  };
}

interface ReconnectingEvent {
  type: 'reconnecting';
  data: {
    attempt: number;      // Current attempt (1, 2, 3...)
    maxAttempts: number;  // Maximum attempts before giving up
  };
}

interface ReconnectedEvent {
  type: 'reconnected';
  data: {
    reconnectedAt: string;   // ISO 8601
    missedMessages: number;  // Messages received while disconnected
  };
}

interface ErrorEvent {
  type: 'error';
  data: { code: string; message: string; recoverable: boolean };
}

interface SessionEndedEvent {
  type: 'ended';
  data: {
    reason: 'completed' | 'forwarded' | 'error';
    endedBy?: 'user' | 'agent' | 'system';
  };
}

Messages

Message Type

Represents a message received from the backend (via WebSocket or recovery).
interface Message {
  /** Unique message identifier */
  eventId: string;
  /** Session/communication ID */
  sessionId: string;
  /** Who sent the message */
  speaker: 'user' | 'agent' | 'system' | 'human_agent';
  /** Language/locale code */
  locale?: string;
  /** Message content */
  data: MessageData;
  /** Custom metadata attached to this message */
  metadata?: Record<string, unknown>;
  /** When the message was sent (ISO 8601) */
  timestamp?: string;
}

interface MessageData {
  type: MessageDataType;
  text?: string;
  mediaUrl?: string;
  fileName?: string;
  action?: MessageAction;
  latitude?: number;
  longitude?: number;
  locationName?: string;
  locationAddress?: string;
}

type MessageDataType =
  | 'text'      // Text content
  | 'image'     // Image with mediaUrl
  | 'document'  // Document with mediaUrl and fileName
  | 'voice'     // Voice message (received only)
  | 'audio'     // Audio file (received only)
  | 'location'  // Location data (received only)
  | 'action';   // System action (forward, end, hangup)

interface MessageAction {
  type: 'forward' | 'end' | 'hangup';
  destination?: string;         // For 'forward': target destination
  metadata?: Record<string, unknown>;
}

Speaker Types

SpeakerDescriptionWhen Used
'user'The end customer using your appMessages sent via session.send()
'agent'The AI agentResponses from the Wonderful agent
'system'System-generated messagesNotifications, status updates
'human_agent'Human support agentWhen session is escalated to human
The SDK uses 'user' while the backend uses 'customer'. This mapping is handled automatically.

Handling Received Media

session.onEvent((event) => {
  if (event.type === 'message') {
    const { data } = event.data;

    switch (data.type) {
      case 'text':
        displayText(data.text);
        break;
      case 'image':
        displayImage(data.mediaUrl);
        break;
      case 'document':
        downloadFile(data.mediaUrl, data.fileName);
        break;
      case 'voice':
      case 'audio':
        playAudio(data.mediaUrl);
        break;
      case 'location':
        showOnMap(data.latitude, data.longitude, data.locationName);
        break;
    }
  }
});

Metadata

The SDK supports two types of metadata: session metadata and message metadata.

Session Metadata

Attached to the entire conversation when starting a session. Useful for tracking context like user info, source app, or business data.
const session = await sdk.chat.start({
  from: 'user-123',
  fromType: 'phone',
  metadata: {
    customer_id: 'cust-456',
    subscription_plan: 'premium',
    source: 'mobile-app',
    app_version: '2.1.0'
  }
});
Session metadata is sent to the backend merged with participant info:
{
  "metadata": {
    "from": "user-123",
    "from_type": "phone",
    "customer_id": "cust-456",
    "subscription_plan": "premium",
    "source": "mobile-app",
    "app_version": "2.1.0"
  }
}

Message Metadata

Attached to individual messages. Useful for tracking intent, context, or business data per message.
// Sending with metadata
await session.send({
  type: 'text',
  text: 'I need help with order #12345',
  metadata: {
    order_id: '12345',
    intent: 'order_inquiry',
    priority: 'high'
  }
});

// Receiving metadata
session.onEvent((event) => {
  if (event.type === 'message') {
    const msg = event.data;
    console.log('Message:', msg.data.text);
    console.log('Metadata:', msg.metadata);
  }
});

Metadata Persistence

TypeStored WithAvailable On Recovery
Session metadataCommunication recordYes (via backend API)
Message metadataEach transcriptionYes (session.messages[].metadata)
Best practices:
  • Use session metadata for user/context info that applies to the whole conversation
  • Use message metadata for per-message context (intent, attachments, actions)
  • Keep metadata JSON-serializable (no functions, circular references)
  • Avoid storing sensitive data (PII) in metadata unless required