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

# Follow real-time chat history (SSE)

> The same real-time state as `GET …/realtime`, pushed over one server-sent events connection instead of polled: the current state is sent right away, then a new `snapshot` event each time it changes — the assistant's reply growing in `streamingMessage`, tool calls, status transitions. Send messages with `POST …/messages?async=true`; opening the stream never runs the model.

Each frame is `event: snapshot` followed by `data: <the same JSON object the realtime endpoint returns>`. Comment frames (`: keep-alive`) arrive every 15 s of silence. The server closes the connection when the status is terminal (`completed`, `error`, `limit_exceeded`) with nothing queued, or after 60 s in any case: if the last snapshot is not terminal, open it again — every frame carries the full state, so nothing is lost. An `event: reconnect` frame precedes a close caused by a server error.

`EventSource` cannot send an `Authorization` header; use `fetch` and read the body. This is what `@devicai/ui` does with `streaming` enabled.

## Reading the stream

`EventSource` cannot send an `Authorization` header, so open the stream with `fetch` and split the body on blank lines. Reopen it while the last snapshot is not terminal: the server closes every connection after 60 seconds. Ask for `?partial=1` so the reply being written arrives as `delta` frames (the appended text) instead of a whole snapshot per write.

```javascript theme={null}
async function followChat(identifier, chatUid, onSnapshot) {
  while (true) {
    const res = await fetch(
      `https://api.devic.ai/api/v1/assistants/${identifier}/chats/${chatUid}/stream?partial=1`,
      { headers: { Authorization: 'Bearer devic-xxx', Accept: 'text/event-stream' } },
    );
    if (!res.headers.get('content-type')?.includes('text/event-stream')) {
      throw new Error(`stream unavailable: ${res.status}`); // fall back to …/realtime
    }
    const reader = res.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';
    let last;
    for (;;) {
      const { value, done } = await reader.read();
      if (done) break;
      buffer += decoder.decode(value, { stream: true });
      let end;
      while ((end = buffer.indexOf('\n\n')) !== -1) {
        const frame = buffer.slice(0, end);
        buffer = buffer.slice(end + 2);
        const data = frame.split('\n').filter((l) => l.startsWith('data:')).map((l) => l.slice(5).trim()).join('');
        if (frame.includes('event: snapshot')) last = JSON.parse(data);
        else if (frame.includes('event: partial') && last) last = { ...last, ...JSON.parse(data) };
        else if (frame.includes('event: delta') && last?.streamingMessage) {
          const m = last.streamingMessage;
          last = { ...last, streamingMessage: { ...m, content: { ...m.content, message: (m.content?.message ?? '') + JSON.parse(data).append } } };
        } else continue; // keep-alive, reconnect
        onSnapshot(last);
      }
    }
    if (last && ['completed', 'error', 'limit_exceeded'].includes(last.status)) return last;
  }
}
```

<Note>
  While `status` is `processing`, `streamingMessage` holds the assistant's reply so far when the model streams (OpenAI, Anthropic, Gemini) and the assistant has no guardrails enabled. Other providers and guarded assistants deliver the text in one piece; status changes and tool calls still arrive as they happen.
</Note>

Poll [`GET …/realtime`](/api-reference/endpoint/get-api-v1-assistants-identifier-chats-chatuid-realtime) instead only when you cannot hold a connection open, such as a serverless function with a short timeout.


## OpenAPI

````yaml GET /v1/assistants/{identifier}/chats/{chatUid}/stream
openapi: 3.0.0
info:
  title: Devic.ai Public API
  description: >-
    Devic.ai is an AI platform that allows you to create, manage, and use AI
    agents for various tasks.
  version: 1.0.0
  contact:
    name: Devic.ai Support
    url: https://devic.ai
  x-logo:
    url: https://devic.ai/logo.png
    altText: Devic.ai Logo
  x-summary: Public API for interacting with Devic.ai platform
servers:
  - url: https://api.devic.ai
    description: Production server
  - url: https://staging-api.devic.ai
    description: Staging server
security:
  - bearerAuth: []
tags:
  - name: Environments
    description: >-
      The machine an agent works on and everything it may reach: sandbox,
      snapshot, knowledge, tools and encrypted variables
  - name: Sandboxes
    description: >-
      Start a real Linux machine on an environment, run commands and files on
      it, and save its snapshot
  - name: Projects
    description: Group agents, assistants, documents and costs into projects
  - name: Documents
    description: >-
      Knowledge base documents: create, version, attach and index markdown
      content for RAG
  - name: Document Folders
    description: Organise knowledge base documents into folders and attach them in bulk
  - name: Files
    description: Upload files and obtain shareable download URLs to attach to messages
  - name: Agents
    description: Endpoints related to AI agents and their operations
  - name: Assistants
    description: Endpoints for interacting with assistants and their specializations
  - name: Tool Servers
    description: Endpoints for managing tool servers and their tool definitions
  - name: Health
    description: API health check endpoints
  - name: Documentation
    description: Endpoints for retrieving markdown documentation
  - name: Integrations
    description: Connect third-party apps and turn them into tools
  - name: Triggers
    description: Start an agent or an assistant from an app event
  - name: Tenant Integrations
    description: Apps that each end user connects for themselves
  - name: Memory
    description: What an assistant remembers between conversations
  - name: Skills
    description: Reusable instruction packs for agents and assistants
  - name: Speech to Text
    description: Audio transcription
  - name: Tenants
    description: Tenants, subtenants and their usage
  - name: MCP Gateway
    description: One MCP endpoint over many servers, with visibility per user
  - name: Tenant Sessions
    description: Tokens that prove which end user is calling
paths:
  /v1/assistants/{identifier}/chats/{chatUid}/stream:
    get:
      tags:
        - Assistants
      summary: Follow real-time chat history (server-sent events)
      description: >-
        The same real-time state as `GET …/realtime`, pushed over one
        server-sent events connection instead of polled: the current state is
        sent right away, then a new `snapshot` event each time it changes — the
        assistant's reply growing in `streamingMessage`, tool calls, status
        transitions. Send messages with `POST …/messages?async=true`; opening
        the stream never runs the model.


        Each frame is `event: snapshot` followed by `data: <the same JSON object
        the realtime endpoint returns>`. Comment frames (`: keep-alive`) arrive
        every 15 s of silence. The server closes the connection when the status
        is terminal (`completed`, `error`, `limit_exceeded`) with nothing
        queued, or after 60 s in any case: if the last snapshot is not terminal,
        open it again — every frame carries the full state, so nothing is lost.
        An `event: reconnect` frame precedes a close caused by a server error.


        `EventSource` cannot send an `Authorization` header; use `fetch` and
        read the body. This is what `@devicai/ui` does with `streaming` enabled.
      operationId: streamRealtimeChat
      parameters:
        - name: identifier
          required: true
          in: path
          description: The unique identifier of the assistant specialization
          schema:
            type: string
        - name: chatUid
          required: true
          in: path
          description: The unique identifier of the chat conversation
          schema:
            type: string
        - name: partial
          required: false
          in: query
          description: >-
            `1` or `true`: while only the reply being written changes, send
            `event: delta` frames with the appended text (`{"append":"…"}`) or
            `event: partial` frames with the whole `streamingMessage`, instead
            of a full snapshot. About half the bytes of polling, against 6-7
            times without it. Older APIs ignore it.
          schema:
            type: string
            enum:
              - '1'
              - 'true'
      responses:
        '200':
          description: >-
            A `text/event-stream` body. `snapshot` events carry a
            `RealtimeChatHistoryDto` (plus `streamingMessage` while the status
            is `processing`).
          content:
            text/event-stream:
              schema:
                type: string
                example: >+
                  event: snapshot

                  data:
                  {"chatUID":"550e8400-e29b-41d4-a716-446655440000","status":"processing","chatHistory":[{"role":"user","content":{"message":"Analyze
                  this
                  data"}}],"streamingMessage":{"role":"assistant","content":{"message":"Based
                  on the an"}},"lastUpdatedAt":1705312200000}


                  : keep-alive


                  event: snapshot

                  data:
                  {"chatUID":"550e8400-e29b-41d4-a716-446655440000","status":"completed","chatHistory":[{"role":"user","content":{"message":"Analyze
                  this data"}},{"role":"assistant","content":{"message":"Based
                  on the analysis..."}}],"lastUpdatedAt":1705312214000}

        '401':
          description: Unauthorized — missing or invalid API key
        '403':
          description: Forbidden — not reachable with this credential
        '404':
          description: Chat not found for this assistant, or no real-time state yet
      security:
        - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Use JWT token for authentication

````