Rensei docs

Activities API

Cursor polling and 3 access modes.

Activities are the unit of observable work from a session - thoughts, tool calls, responses, and errors. The activity model is defined by the OSS runtime; see donmai.dev/docs for the schema. This page covers the Rensei platform's activity reading endpoint and its three access modes.

The Activities API (GET /api/public/session-activities) lets you poll or tail the activity feed of any session. It supports three distinct authentication modes to cover daemon workers, unauthenticated TUI viewers, and authenticated CLI/dashboard clients.

Endpoint

GET /api/public/session-activities?sessionId=<id>[&cursor=<cursor>]

sessionId is required. cursor is optional; when supplied, only activities with IDs after the cursor are returned, enabling efficient polling.

Response Shape

{
  "activities": [
    {
      "id": "42",
      "type": "thought",
      "body": "I need to read the auth module before editing it.",
      "createdAt": "2026-06-02T14:23:01Z"
    },
    {
      "id": "43",
      "type": "action",
      "body": "{\"tool\":\"Read\",\"input\":{\"file_path\":\"/src/auth/index.ts\"}}",
      "createdAt": "2026-06-02T14:23:02Z"
    }
  ],
  "cursor": "43",
  "sessionStatus": "working"
}

cursor is the ID of the last returned activity. Pass it as ?cursor=43 on the next poll to get only newer activities. When the session reaches a terminal status (completed, failed, stopped), sessionStatus reflects that and subsequent polls return an empty activities array.

Session Status Mapping

The sessionStatus field uses a simplified public vocabulary:

Public statusInternal status
queuedpending
workingclaimed or running
completedcompleted
failedfailed
stoppedstopped

Three Access Modes

The endpoint authenticates requests differently depending on how you identify the session:

Mode 3: Authenticated CLI or Dashboard

Use a rsk_* Bearer token or session cookie. The sessionId parameter accepts either:

  • The raw trackerSessionId (held only by the daemon/worker)
  • The 16-character hashed ID returned by /api/public/sessions - this is what rensei session list shows and what the dashboard links use

The platform performs a reverse-lookup when the session isn't found directly, scanning the session store and matching by hash. Org scoping is enforced: tokens can only read sessions belonging to their org.

# Using the hashed public ID from `rensei session list`
curl "https://app.rensei.ai/api/public/session-activities?sessionId=a1b2c3d4e5f6a7b8" \
  -H "Authorization: Bearer rsk_live_…"

This is the most common mode for integrations and automation.

Mode 2: Worker JWT

Daemon workers authenticate with a runtime JWT (rsk_* or a worker registration token). They pass the raw trackerSessionId - the same ID they received from the poll/claim cycle. The platform adds a scoping assertion to ensure a worker JWT can only read activities for sessions within its assigned project.

# Daemon internal - raw trackerSessionId
curl "https://app.rensei.ai/api/public/session-activities?sessionId=<raw-tracker-session-id>" \
  -H "Authorization: Bearer <worker-jwt>"

Worker auth tokens are issued by the daemon registration flow; see the Worker Protocol reference for details.

Mode 1: Public Hash (Unauthenticated TUI Viewer)

A TUI viewer that knows the raw trackerSessionId can prove read access without full org authentication by supplying a SHA-256 HMAC of the raw ID as sessionHash:

GET /api/public/session-activities
  ?sessionId=<raw-trackerSessionId>
  &sessionHash=<sha256-hmac-of-sessionId>

The sessionHash must be the 32-character hex value computed as:

sha256("session:" + rawSessionId).slice(0, 32)

The TUI computes this automatically. This mode is intentionally separate from the authenticated CLI mode - it requires the caller to already know the raw ID (which only the daemon holds), making it safe to expose to local TUI tools without an org token.

# TUI-style unauthenticated access - requires raw session ID + valid hash
curl "https://app.rensei.ai/api/public/session-activities\
?sessionId=<raw-id>&sessionHash=<hmac>"

There are two separate hash functions for session IDs in the platform. Mode 1 (public hash / TUI) uses a 32-character HMAC hash ("session:" + id). Mode 3 (CLI/dashboard) uses the 16-character SHA-256 prefix that /api/public/sessions returns. These are not interchangeable - passing a 16-char hash as sessionId in Mode 1 will result in a 404.

Cursor-Based Polling Pattern

The intended pattern for rensei session stream and custom integrations:

async function* streamActivities(sessionId: string, token: string) {
  let cursor: string | undefined
  while (true) {
    const url = new URL('https://app.rensei.ai/api/public/session-activities')
    url.searchParams.set('sessionId', sessionId)
    if (cursor) url.searchParams.set('cursor', cursor)

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${token}` },
    })
    const data = await res.json()

    for (const activity of data.activities) {
      yield activity
    }

    cursor = data.cursor

    // Stop when terminal
    if (['completed', 'failed', 'stopped'].includes(data.sessionStatus)) {
      return
    }

    // Poll interval - adjust to taste
    await new Promise((r) => setTimeout(r, 2000))
  }
}

Storage: Ring Buffer + Postgres Fallback

Activities are served from two backing stores, tried in order:

  1. In-process ring buffer - the fastest path. Populated as the session runs within the current server process. Limited by MAX_ACTIVITIES_PER_SESSION.
  2. Postgres fallback - when the ring buffer is empty (after a dev-server reload, or if the buffer was trimmed), the platform falls back to session_activities rows in Postgres. This ensures rensei session stream never silently returns nothing after a server restart.

The Postgres fallback is transparent - you will not see a different response shape, only a potential increase in latency on the first poll after a cold start.

SSE (Server-Sent Events)

For live canvas updates in the workflow editor, the platform also exposes GET /api/sessions/[id]/stream as a true SSE endpoint. This is distinct from the polling Activities API - it is used by the Session Inspector and the workflow execution panel, not by CLI polling.

On this page