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 status | Internal status |
|---|---|
queued | pending |
working | claimed or running |
completed | completed |
failed | failed |
stopped | stopped |
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 whatrensei session listshows 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:
- In-process ring buffer - the fastest path. Populated as the session runs within the current server process. Limited by
MAX_ACTIVITIES_PER_SESSION. - 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_activitiesrows in Postgres. This ensuresrensei session streamnever 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.
Related Pages
- Interactive Chat - send prompts or stop sessions
- Session Detail - activity stream in the UI
- Session Inspector - full tool call history and context browser
- Context Derivation - how tool calls become structured context facets