Public Sessions API
Public stats/sessions/prompt/stop endpoints.
The public API exposes unauthenticated or minimally-authenticated endpoints for monitoring dashboards, chat consumers, and session-level interactions. No PII is exposed - sessions are identified by opaque IDs and hash tokens.
Authentication model
Public endpoints use one of three access patterns:
| Pattern | Endpoints | Usage |
|---|---|---|
| Cookie or Bearer rsk_* | GET /api/public/stats, GET /api/public/sessions, activity/metrics routes | Dashboard users and CLI operators |
| Session hash | GET /api/public/sessions/[id], prompt, stop | Embed viewers, chat consumers without a full API key |
Despite the /api/public prefix, none of these endpoints are anonymous - "public" means org-scoped read access outside the app shell, not unauthenticated.
Session hash
The session hash is a 32-character hex string derived as SHA-256("session:{sessionId}").slice(0, 32). It lets callers access a single session without a full API key, enabling safe embedding.
import { createHash } from 'crypto';
function publicSessionHash(sessionId: string): string {
return createHash('sha256')
.update(`session:${sessionId}`)
.digest('hex')
.slice(0, 32);
}The public session hash (32 chars, session: prefix) is different from the internal worker-protocol session hash (16 chars, no prefix). Do not confuse them - using the wrong hash returns 401 Unauthorized.
Fleet statistics
GET /api/public/statsRequires a session cookie or rsk_ API key; stats are scoped to the caller's active org.
Response
{
"workersOnline": 4,
"agentsWorking": 2,
"queueDepth": 1,
"completedToday": 18,
"availableCapacity": 14,
"totalCostToday": 3.24,
"totalCostAllTime": 142.80,
"sessionCountToday": 18,
"timestamp": "2026-06-02T12:00:00Z"
}This endpoint is safe to call from a public status page or marketing dashboard - it returns aggregate counts with no per-session or per-user data.
Session list
GET /api/public/sessions
Authorization: Bearer rsk_live_<key> (or session cookie)Returns an enriched list of sessions for a project with health summaries.
Query parameters
| Parameter | Description |
|---|---|
projectId | Platform project ID. Takes precedence over project. |
project | Project slug. Use the slug (not the display name) - see note below. |
status | Filter by status: running, completed, failed, stopped |
limit | Maximum results (default: 50, max: 200) |
cursor | Pagination cursor from a previous response |
Pass ?project=<slug> using the project slug (URL-safe identifier), not the display name. The display name is not looked up - a mismatch returns an empty result set silently.
Response
{
"sessions": [
{
"sessionId": "sess_01abc...",
"status": "running",
"workType": "feature",
"issueName": "Add dark mode support",
"issueUrl": "https://linear.app/org/issue/ENG-123",
"workerId": "wkr_01abc...",
"agentId": "agent_01abc...",
"startedAt": "2026-06-02T12:00:00Z",
"health": "healthy",
"costSoFar": 0.42
}
],
"nextCursor": "cursor_abc..."
}Single session
GET /api/public/sessions/{sessionId}Access a single session by ID. Requires either:
- A cookie session or
Bearer rsk_*key (normal auth), or ?hash=<32-char-hash>derived from the public session hash function above
Example with hash
SESSION_ID="sess_01abc..."
HASH=$(node -e "
const {createHash} = require('crypto');
console.log(createHash('sha256').update('session:' + process.env.SESSION_ID).digest('hex').slice(0,32));
" SESSION_ID="$SESSION_ID")
curl "https://app.rensei.ai/api/public/sessions/$SESSION_ID?hash=$HASH"Response
{
"sessionId": "sess_01abc...",
"status": "running",
"workType": "feature",
"issueName": "Add dark mode support",
"issueUrl": "https://linear.app/org/issue/ENG-123",
"activities": [
{
"type": "thought",
"content": "Reading the issue context...",
"timestamp": "2026-06-02T12:01:00Z"
}
],
"health": "healthy",
"startedAt": "2026-06-02T12:00:00Z"
}Send a prompt (nudge)
Push a text prompt to a running agent's inbox. The agent processes it as a nudge signal on its next turn.
POST /api/public/sessions/{sessionId}/prompt
Content-Type: application/jsonAuthentication: session hash required - pass ?hash=<hash> query parameter.
Request body
{
"text": "Focus on the error handling in the authentication flow, not the UI."
}Response
{ "ok": true, "messageId": "msg_01abc..." }The message is placed into the session's Redis inbox stream. The agent's next poll ACKs it. See Poll & Heartbeat for how inbox messages are delivered to workers.
Stop a session
Send a stop signal to a running session. The agent processes it gracefully - completing its current step, posting a summary, then transitioning to stopped.
POST /api/public/sessions/{sessionId}/stopAuthentication: session hash required - pass ?hash=<hash> query parameter.
Response
{ "ok": true }Stop is a graceful signal, not a kill. The agent may take up to one full turn cycle to honor it. If you need immediate termination, contact support.
Session activities time-series
GET /api/public/session-activities
Authorization: Bearer rsk_live_<key>Returns a time-series of activity events across sessions with cursor-based polling. Useful for analytics dashboards and real-time monitoring.
Query parameters
| Parameter | Description |
|---|---|
sessionId | Required. Linear session ID (raw or hashed form from the session list response). |
from | ISO 8601 start time |
to | ISO 8601 end time |
type | Filter by activity type: thought, action, response, error |
cursor | Pagination cursor from a previous response |
This endpoint supports three access modes: worker JWT (Authorization: Bearer <runtimeJwt>), public hash (?sessionId=<id>&sessionHash=<32-char-hash>), and authenticated CLI/dashboard (Bearer rsk_* or cookie). Use the public-hash mode to embed a live activity feed without exposing a full API key.
Example
curl "https://app.rensei.ai/api/public/session-activities?sessionId=sess_01abc..." \
-H "Authorization: Bearer rsk_live_..."Response
{
"activities": [
{
"type": "thought",
"content": "Reading the failing test to understand the root cause.",
"timestamp": "2026-06-02T12:01:00Z"
},
{
"type": "action",
"content": "Running: npm test --testPathPattern=auth",
"timestamp": "2026-06-02T12:01:30Z"
},
{
"type": "response",
"content": "Fixed the null pointer in UserService.getById. PR #42 is ready.",
"timestamp": "2026-06-02T12:08:00Z"
}
],
"cursor": "cursor_abc...",
"sessionStatus": "completed"
}Routing metrics
GET /api/public/routing-metrics
Authorization: Bearer rsk_live_<key>Returns MAB (Multi-Armed Bandit) routing posteriors and recent routing decisions. Use this to understand provider selection rates, confidence scores, and code-survival signal counts.
Query parameters
| Parameter | Description |
|---|---|
from | ISO 8601 start time for recent decisions |
to | ISO 8601 end time |
limit | Max recent decisions to return (default: 50) |
cursor | Pagination cursor |
Response
{
"posteriors": [
{
"providerId": "anthropic.claude-sonnet-4-5",
"successCount": 142,
"failureCount": 3,
"confidence": 0.97,
"totalObservations": 145
}
],
"recentDecisions": [
{
"sessionId": "sess_01abc...",
"providerId": "anthropic.claude-sonnet-4-5",
"timestamp": "2026-06-02T12:00:00Z"
}
],
"summary": {
"totalObservations": 145,
"routingEnabled": true,
"explorationRate": 0.1,
"avgConfidence": 0.97,
"survivalRewardCount": 12
},
"timestamp": "2026-06-02T12:00:00Z"
}Phase metrics
GET /api/public/phase-metrics
Authorization: Bearer rsk_live_<key>Returns SDLC phase-level aggregated cost, cycle time, and rework stats across your workspace. Useful for engineering productivity dashboards and sprint retrospectives.
Query parameters
| Parameter | Description |
|---|---|
timeRange | Aggregation window: 7d, 30d, or 90d (default: 30d) |
Response
{
"timeRange": "30d",
"phases": {
"development": {
"avgCycleTimeMs": 1800000,
"avgCostUsd": 0.84,
"avgAttempts": 1.2,
"totalRecords": 48
},
"qa": {
"avgCycleTimeMs": 600000,
"avgCostUsd": 0.31,
"avgAttempts": 1.0,
"totalRecords": 44
},
"refinement": {
"avgCycleTimeMs": 900000,
"avgCostUsd": 0.45,
"avgAttempts": 1.1,
"totalRecords": 10
},
"acceptance": {
"avgCycleTimeMs": 300000,
"avgCostUsd": 0.12,
"avgAttempts": 1.0,
"totalRecords": 40
}
},
"reworkRate": 0.08,
"escalationDistribution": {
"normal": 38,
"context-enriched": 6,
"decompose": 3,
"escalate-human": 1
},
"issueCount": 48,
"timestamp": "2026-06-02T12:00:00Z"
}| Field | Description |
|---|---|
phases | Per-phase aggregates: average cycle time (ms), cost (USD), re-attempt count, and total records |
reworkRate | Fraction of issues that required a refinement or re-attempt pass (0-1) |
escalationDistribution | Breakdown of escalation types across the time window |
issueCount | Total issues completed in the time range |
TypeScript SDK example
async function monitorSession(sessionId: string): Promise<void> {
const hash = createHash('sha256')
.update(`session:${sessionId}`)
.digest('hex')
.slice(0, 32);
const res = await fetch(
`https://app.rensei.ai/api/public/sessions/${sessionId}?hash=${hash}`
);
const session = await res.json();
console.log(`Session ${sessionId}: ${session.status}`);
console.log(`Activities: ${session.activities.length}`);
}Related pages
- Sessions overview - project-scoped session list in the dashboard
- Interactive Chat - prompt and stop via the CLI TUI
- Session Lifecycle - server-side session FSM