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

# WebSockets

> Stream inbox chat events in real time over the organization inbox WebSocket.

Example for connecting to the **organization inbox** WebSocket using an API key. This is the supported, API-exposed route for real-time chat events across an org (same room the app uses for inbox / assistant views via `/orgs/{org_id}/inbox/ws`).

Connect to `GET` (upgrade) at:

`{API_URL}/orgs/:org_id/inbox/ws`

`{API_URL}` is the same base URL as in your OpenAPI spec (for example `https://api.useinvent.com`). Paths are appended as-is; if your environment uses an extra prefix, include it in `{API_URL}`.

```typescript theme={"system"}
import { WebSocket } from 'ws'; // Node.js ws library

const orgId = 'org_123';
const apiKey = 'your-api-key-token'; // Settings → API Keys
const apiBase = 'https://api.useinvent.com';
const wsUrl = `${apiBase.replace(/^http/, 'ws')}/orgs/${orgId}/inbox/ws`;

const ws = new WebSocket(wsUrl, {
  headers: {
    Authorization: `Bearer ${apiKey}`,
  },
});

ws.on('open', () => {
  console.log('Connected to org inbox WS');
});

ws.on('message', (data) => {
  const event = JSON.parse(data.toString());
  console.log('Chat event:', event);
});

ws.on('error', (error) => {
  console.error('WebSocket error:', error);
});

ws.on('close', () => {
  console.log('Disconnected from org inbox WS');
});
```

Related HTTP endpoint for listing inbox chats: `GET /orgs/:org_id/inbox`.

## Event Schemas

All events follow the `ChatEventSchema` discriminated union format:

```typescript theme={"system"}
type ChatEvent =
  | { type: 'create'; data: ChatSchema; identifier: string | null }
  | { type: 'update'; data: ChatSchema }
  | { type: 'typing'; chat_id: string; member_id: string }
  | { type: 'message'; chat_id: string; data: ChatMessageSchema }
  | { type: 'message-part'; chat_id: string; message_id: string; data: ModelMessagePartSchema }
  | { type: 'delete-message'; chat_id: string; message_id: string }
  | { type: 'message-reaction'; chat_id: string; message_id: string; member_id?: string; emoji: string; action: 'react' | 'unreact' }
  | { type: 'delete'; chat_id: string }
  | { type: 'tier-limits'; tier: ModelTierSchema; data: ChatLimitSchema }
  | { type: 'tool-limits'; tool: string; data: ChatLimitSchema }
  | { type: 'conversation-memory-update'; chat_id: string; data: ConversationMemorySchema }
  | { type: 'conversation-memory-delete'; chat_id: string; memory_id: string };
```

## Handling Streaming Messages

Messages are streamed in parts, then a complete message event is sent when finished.

### During Streaming: `message-part` Events

During streaming, you'll receive multiple `message-part` events:

```typescript theme={"system"}
ws.on('message', (data) => {
  const event = JSON.parse(data.toString());

  if (event.type === 'message-part') {
    // Accumulate streaming parts
    // data is { id, role, part }: role is 'assistant' for model parts, 'tool' for results
    const { chat_id, message_id, data } = event;
    const part = data.part;

    // Part types include:
    // - text_delta: Streaming text chunks
    // - tool_call: Tool invocation
    // - tool_result: Tool execution result
    // - text: Complete text (after streaming)
    // - file, image: File attachments
    // - reasoning: Model reasoning steps

    if (part.type === 'text_delta') {
      // Append streaming text
      console.log('Streaming text:', part.text);
    }
  }
});
```

### After Streaming: Complete `message` Event

When streaming completes, you'll receive a complete `message` event with the full message:

```typescript theme={"system"}
ws.on('message', (data) => {
  const event = JSON.parse(data.toString());

  if (event.type === 'message') {
    // Complete message with all parts
    const { chat_id, data: message } = event;

    // message contains:
    // - id: Message ID
    // - role: 'user' | 'assistant' | 'system' | 'tool' | 'event' | 'note'
    // - model: Model used
    // - messages: Array of complete message parts
    // - status: 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELED'
    // - usage: Token usage information
    // - error: Error message if failed

    console.log('Complete message:', message);
  }
});
```

### Reactions: `message-reaction` Event

A `message-reaction` event arrives when someone reacts to a message with an emoji, or removes a reaction (`action` is `react` or `unreact`). `member_id` names the team member who reacted and is absent when the assistant reacted.

### Example: Accumulating Streamed Parts

```typescript theme={"system"}
const streamingMessages = new Map<string, string>();

ws.on('message', (data) => {
  const event = JSON.parse(data.toString());

  switch (event.type) {
    case 'message-part': {
      const { message_id, data: part } = event;

      if (part.type === 'text_delta') {
        // Accumulate streaming text
        const current = streamingMessages.get(message_id) || '';
        streamingMessages.set(message_id, current + part.text);
        console.log('Current text:', current + part.text);
      }
      break;
    }

    case 'message': {
      // Complete message received - use this as source of truth
      const { data: message } = event;
      streamingMessages.delete(message.id);

      // Full message with all parts is now available
      console.log('Final message:', message.messages);
      break;
    }
  }
});
```

## Notes

* Authenticate with an API key: `Authorization: Bearer <token>`.
* Create keys under [Settings → API Keys](/workspace-management/api-keys).
* `GET /orgs/:org_id/inbox/ws` is registered as an API endpoint (`isApiEndpoint: true`) and appears in the public OpenAPI document.
* `message-part` events are sent during streaming for real-time updates.
* The final `message` event is the source of truth after streaming; prefer it over only accumulated parts.
