Rensei docs
Public

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:

PatternEndpointsUsage
Cookie or Bearer rsk_*GET /api/public/stats, GET /api/public/sessions, activity/metrics routesDashboard users and CLI operators
Session hashGET /api/public/sessions/[id], prompt, stopEmbed 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/stats

Requires 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

ParameterDescription
projectIdPlatform project ID. Takes precedence over project.
projectProject slug. Use the slug (not the display name) - see note below.
statusFilter by status: running, completed, failed, stopped
limitMaximum results (default: 50, max: 200)
cursorPagination 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/json

Authentication: 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}/stop

Authentication: 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

ParameterDescription
sessionIdRequired. Linear session ID (raw or hashed form from the session list response).
fromISO 8601 start time
toISO 8601 end time
typeFilter by activity type: thought, action, response, error
cursorPagination 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

ParameterDescription
fromISO 8601 start time for recent decisions
toISO 8601 end time
limitMax recent decisions to return (default: 50)
cursorPagination 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

ParameterDescription
timeRangeAggregation 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"
}
FieldDescription
phasesPer-phase aggregates: average cycle time (ms), cost (USD), re-attempt count, and total records
reworkRateFraction of issues that required a refinement or re-attempt pass (0-1)
escalationDistributionBreakdown of escalation types across the time window
issueCountTotal 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}`);
}

On this page