Skip to content
Go to Dashboard

SSE Progress Subscription

This page explains how to subscribe to Memory processing progress with SSE (Server-Sent Events) after calling POST /api/sessions/{session_id}/messages to write a batch of Message input.

SSE is useful for observing asynchronous processing status, such as whether Messages are written, Facts are generated, Summaries are generated, or Topics are updated. It does not return raw Message content, Fact content, or Summary content.

Before You Start

  • You have created a GUMem Project and have a server-side API Key.
  • You have created a Session and stored its session_id.
  • Your server can store the request_id returned by the Message write response.
  • If you need to wait for facts, summaries, or topics, the background worker, Elasticsearch, LLM, and embedding configuration should be available.

Native browser EventSource cannot set an Authorization header directly. In production, keep the API Key on your server and proxy the SSE stream, or use a fetch stream / SSE client that supports custom headers.

Call Flow

  1. Write a batch of Message input.
  2. Read data.request_id from the write response.
  3. Subscribe to SSE with the same session_id and request_id.
  4. Update your application state based on progress, completed, timeout, or error events.

Write Messages

Call the Message write endpoint:

bash
export GUMEM_BASE_URL="http://localhost:8000"
export GUMEM_API_KEY="<api_key>"
export GUMEM_SESSION_ID="session_xxx"

curl -sS -X POST "$GUMEM_BASE_URL/api/sessions/$GUMEM_SESSION_ID/messages" \
  -H "Authorization: Api-Key $GUMEM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "content": "For team scheduling, use Berlin when I mention Europe."
      },
      {
        "role": "assistant",
        "content": "Got it. I will use Berlin for Europe scheduling."
      }
    ]
  }'

The data.request_id in the response is the progress subscription ID for this Message batch:

json
{
  "code": 0,
  "message": "success",
  "data": {
    "request_id": "req_abc123def4567890"
  }
}

All Messages written by the same request share one request_id. If you write an empty Message list, request_id may be null; do not start an SSE subscription in that case.

Subscribe To Progress

Use GET /api/sessions/{session_id}/messages/sse to subscribe to processing progress:

bash
export GUMEM_REQUEST_ID="req_abc123def4567890"

curl -N "$GUMEM_BASE_URL/api/sessions/$GUMEM_SESSION_ID/messages/sse?request_id=$GUMEM_REQUEST_ID&wait_until=topics&timeout_seconds=120" \
  -H "Authorization: Api-Key $GUMEM_API_KEY" \
  -H "Accept: text/event-stream"

Query parameters:

ParameterRequiredDefaultDescription
request_idYes-data.request_id returned by the Message write response.
wait_untilNotopicsStage to wait for before sending completed and closing the stream. Allowed values: messages, facts, summaries, topics.
poll_interval_msNo1000Server-side progress polling interval. Range: 250 to 5000 milliseconds.
timeout_secondsNo120Maximum wait time. Range: 1 to 600 seconds.
include_logsNofalseWhether to include server-side SSE log events for the same request_id. Usually only useful for debugging.

Authentication is the same as other GUMem APIs:

http
Authorization: Api-Key <api_key>
Accept: text/event-stream

The response is not a one-time JSON body. It is a continuous text/event-stream. Each event has an event line and a data line: event is the event type, and data is a JSON string that contains the current processing stage and counters. Parse events with an SSE parser, then deserialize data as JSON.

text
event: progress
data: <json>

After deserializing the data string, the JSON object has this shape:

json
{
  "request_id": "req_abc123def4567890",
  "session_id": "session_xxx",
  "stage": "messages_ready",
  "wait_until": "topics",
  "message_count": 2,
  "processed_message_count": 0,
  "facts_count": 0,
  "summaries_count": 0,
  "topics_count": 0,
  "completed": false,
  "timestamp": "2026-05-07T12:00:00.000000+00:00"
}

Response parts:

PartDescription
eventSSE event type, such as progress, completed, timeout, error, or log.
dataJSON string. See Payload Fields for field descriptions.
: keep-aliveSSE comment line. It means the connection is still alive but the state has not changed; clients can usually ignore it.

Processing Stages

wait_until uses public stage names:

text
messages -> facts -> summaries -> topics
StageSSE stageDescription
messagesmessages_readyThis Message batch is written and can be found by the progress query.
factsfacts_readyFacts have been generated from this Message batch.
summariessummaries_readySummaries have been generated from the Facts.
topicstopics_readyAt least one Topic has been extracted from the Summaries.

If request_id cannot be found yet, SSE returns not_started and keeps waiting until the request times out or the batch appears.

Event Types

EventTriggerConnection behavior
progressStage or count changes.Keeps the stream open.
completedCurrent stage reaches wait_until.Sends the event and closes the stream.
timeouttimeout_seconds is reached before completion.Sends the latest state and closes the stream.
errorAn unhandled error happens after the stream starts.Sends an error payload and closes the stream.
loginclude_logs=true and logs exist for the same request_id.Keeps the stream open.
: keep-aliveState has not changed.Keeps the stream open.

Example events:

text
event: progress
data: <json>
json
{
  "request_id": "req_abc123def4567890",
  "session_id": "session_xxx",
  "stage": "messages_ready",
  "wait_until": "topics",
  "message_count": 2,
  "processed_message_count": 0,
  "facts_count": 0,
  "summaries_count": 0,
  "topics_count": 0,
  "completed": false,
  "timestamp": "2026-05-07T12:00:00.000000+00:00"
}
text
event: completed
data: <json>
json
{
  "request_id": "req_abc123def4567890",
  "session_id": "session_xxx",
  "stage": "topics_ready",
  "wait_until": "topics",
  "message_count": 2,
  "processed_message_count": 2,
  "facts_count": 1,
  "summaries_count": 1,
  "topics_count": 1,
  "completed": true,
  "timestamp": "2026-05-07T12:00:08.000000+00:00"
}

Payload Fields

SSE payloads return stage state and counts only.

FieldDescription
request_idMessage batch write ID.
session_idSession ID from the URL.
stageCurrent processing stage. Possible values: not_started, messages_ready, facts_ready, summaries_ready, or topics_ready.
wait_untilTarget stage for this subscription.
message_countNumber of Messages in the batch.
processed_message_countNumber of Messages in the batch with processed status.
facts_countNumber of Facts matched to this Message batch.
summaries_countNumber of Summaries matched to this batch's Facts.
topics_countNumber of unique Topics extracted from the Summaries.
completedWhether the current stage has reached wait_until.
timestampServer UTC time.

Server-Side Integration Example

If your frontend needs to show progress, proxy the SSE stream from your application server so the GUMem API Key is not exposed to the browser.

ts
async function streamMessageProgress(params: {
  baseUrl: string;
  apiKey: string;
  sessionId: string;
  requestId: string;
}) {
  const url = new URL(
    `/api/sessions/${params.sessionId}/messages/sse`,
    params.baseUrl
  );
  url.searchParams.set("request_id", params.requestId);
  url.searchParams.set("wait_until", "topics");
  url.searchParams.set("timeout_seconds", "120");

  const response = await fetch(url, {
    headers: {
      Authorization: `Api-Key ${params.apiKey}`,
      Accept: "text/event-stream",
    },
  });

  if (!response.ok || !response.body) {
    throw new Error(`SSE request failed: ${response.status}`);
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    const chunk = decoder.decode(value, { stream: true });
    process.stdout.write(chunk);
  }
}

Failure Handling

  • 401: API Key is missing or invalid. Check Authorization: Api-Key <api_key>.
  • 422: request_id is missing or query parameters are outside the allowed range.
  • timeout event: the stream did not reach wait_until in time. Use an earlier wait_until stage or increase timeout_seconds.
  • Stuck at messages_ready: Messages are written, but later Facts / Summaries / Topics processing has not completed. Check the background worker, Elasticsearch, LLM, and embedding configuration.
  • Network disconnect: subscribe again with the same request_id. SSE is an observation channel and should not be the only business completion signal.

Checkpoint

In local environments without complete LLM / embedding configuration, start with wait_until=messages to verify the SSE connection and authentication. In production, wait for facts, summaries, or topics based on your workflow.

Next Step

Read Add Memory to write Message input, or Query Memory to recall Memory in later requests.