Rensei docs
Worker Protocol

Session Lifecycle

FSM, status/activity/completion endpoints.

Session lifecycle endpoints allow workers to report execution progress, transition session state, post activity to Linear, and finalize sessions. All endpoints require Authorization: Bearer <runtimeJwt>.

State machine

Transitions are enforced server-side. Attempting an illegal transition (e.g. queuedcompleted) returns 409 Conflict.

Endpoint reference

Status read

GET /api/sessions/{sessionId}/status
Authorization: Bearer <runtimeJwt>

Returns the current session state:

{
  "sessionId": "sess_01abc...",
  "status": "running",
  "workerId": "wkr_01abc...",
  "startedAt": "2026-06-02T12:00:00Z",
  "updatedAt": "2026-06-02T12:05:00Z"
}

Status update (FSM transition)

POST /api/sessions/{sessionId}/status
Authorization: Bearer <runtimeJwt>
Content-Type: application/json

Request body

{
  "status": "finalizing",
  "reason": "agent_complete"
}

Valid status values and valid preceding states:

Target statusValid fromDescription
runningqueuedWorker has started executing
finalizingrunningWorker is wrapping up (writing artifacts, posting completion)
completedfinalizingExecution finished successfully
failedrunning, finalizingExecution failed
stoppedrunningStop signal was processed

Post activity

Post a structured activity entry to the session. Activities are forwarded to the Linear AgentSession thread.

POST /api/sessions/{sessionId}/activity
Authorization: Bearer <runtimeJwt>
Content-Type: application/json

Request body

{
  "type": "thought",
  "content": "Analyzing the failing test case to understand the root cause.",
  "metadata": {}
}
Activity typeDescription
thoughtInternal reasoning step (not shown to end users by default)
actionTool invocation or file operation
responseMessage addressed to the user; transitions Linear session to complete
errorError condition; use for recoverable errors

Sending a response activity is the correct way to close a Linear AgentSession. The Linear API has no explicit "close" mutation - the platform infers active → complete when it receives a response activity. See the Linear integration guide for detail.

Post progress

Post a milestone message (shown in the session detail view):

POST /api/sessions/{sessionId}/progress
Authorization: Bearer <runtimeJwt>
Content-Type: application/json
{
  "message": "Tests passing. Opening pull request.",
  "phase": "qa"
}

Post completion

Post the final completion summary. Long summaries are automatically chunked into Linear comment threads:

POST /api/sessions/{sessionId}/completion
Authorization: Bearer <runtimeJwt>
Content-Type: application/json
{
  "summary": "## Summary\n\nFixed the null pointer dereference in `UserService.getById`...",
  "pullRequestUrl": "https://github.com/my-org/my-repo/pull/42",
  "artifacts": [
    { "type": "pr", "url": "https://github.com/my-org/my-repo/pull/42" }
  ]
}

Lock refresh

Sessions hold an issue lock (prevents parallel agents from claiming the same issue). Refresh it to extend the TTL during long-running operations:

POST /api/sessions/{sessionId}/lock-refresh
Authorization: Bearer <runtimeJwt>

Call this approximately every 60 seconds during active execution. The platform automatically releases the lock when the session reaches a terminal state.

Report tool error

Post a tool execution error (e.g. a bash command that exited non-zero, a file read failure):

POST /api/sessions/{sessionId}/tool-error
Authorization: Bearer <runtimeJwt>
Content-Type: application/json
{
  "toolName": "bash",
  "error": "Command exited with code 1: npm test",
  "stdout": "...",
  "stderr": "npm ERR! Test suite failed to run"
}

Security scan event

Post a security scan finding. Triggers compliance review workflows when BFSI mode is enabled:

POST /api/sessions/{sessionId}/security-scan
Authorization: Bearer <runtimeJwt>
Content-Type: application/json
{
  "severity": "high",
  "finding": "Potential SQL injection in UserRepository.search",
  "file": "src/repositories/user.ts",
  "line": 42
}

Transfer ownership

Hand the session to another worker (used during rolling deploys):

POST /api/sessions/{sessionId}/transfer-ownership
Authorization: Bearer <runtimeJwt>
Content-Type: application/json
{
  "targetWorkerId": "wkr_02def..."
}

Recovery packet

Fetch a crash-recovery context packet. Used by workers that restart mid-session:

GET /api/sessions/{sessionId}/recovery-packet
Authorization: Bearer <runtimeJwt>

Returns serialized session state sufficient to resume an interrupted agent.

Routing provider select

Ask the platform to sample the MAB (Multi-Armed Bandit) posterior store and pick the best provider for this session. The in-process daemon calls this at session start when ROUTING_SELECTOR_ENABLED=true. The endpoint is dark by default - when routing is disabled or an explicit provider was already pinned, it returns { selectedProvider: null, source: "disabled" | "explicit" } without touching Redis so the daemon falls back to its statically resolved provider.

POST /api/sessions/{sessionId}/routing-select
Authorization: Bearer <runtimeJwt>
Content-Type: application/json
{
  "workType": "bug_fix",
  "candidates": ["anthropic.claude-sonnet-4-5", "anthropic.claude-haiku-3-5"],
  "project": "my-project"
}

Response

{
  "selectedProvider": "anthropic.claude-sonnet-4-5",
  "source": "mab-routing"
}

Routing feedback

Post a routing observation at session end. The daemon calls this to feed MAB reward signals back to the posterior store so future routing decisions improve over time. Best-effort: errors are swallowed with 200 OK so a Redis blip never fails the worker's terminal flow.

POST /api/sessions/{sessionId}/routing-feedback
Authorization: Bearer <runtimeJwt>
Content-Type: application/json
{
  "provider": "anthropic.claude-sonnet-4-5",
  "workType": "bug_fix",
  "reward": 1.0,
  "taskCompleted": true,
  "prCreated": true,
  "qaResult": "passed",
  "totalCostUsd": 0.84,
  "wallClockMs": 1800000
}
FieldTypeRequiredDescription
providerstringYesProvider ID that ran the session
workTypestringNoWork type (e.g. bug_fix, feature)
rewardnumberNoExplicit reward override (0-1). Omit to let the platform calculate from task/PR/QA signals.
taskCompletedbooleanNoWhether the agent completed the task
prCreatedbooleanNoWhether a pull request was opened
qaResultstringNoQA pass/fail result: passed, failed, unknown
totalCostUsdnumberNoTotal cost in USD for this session
wallClockMsnumberNoTotal wall-clock duration in milliseconds

SSE event stream

Subscribe to a real-time stream of org-wide session events. This endpoint is designed for the Rensei dashboard and requires a browser session cookie - it is not accessible with a runtime JWT.

GET /api/sessions/stream
Cookie: <session cookie>

Emits Server-Sent Events (SSE) for all sessions in the authenticated user's org. Events are filtered to remove internal hook-bus messages (provider_hook_event) before reaching the client.

Event types

Event typeDescription
session_createdA new session was queued
session_status_changedA session transitioned state (e.g. queuedrunning)
session_activityA new activity was posted to a session
session_completedA session reached a terminal state
session_health_updatedSession health indicator changed
heartbeatKeep-alive event emitted every 15 seconds

Example event

event: session_status_changed
data: {"type":"session_status_changed","sessionId":"sess_01abc...","orgId":"org_01abc...","timestamp":"2026-06-02T12:05:00Z","payload":{"from":"queued","to":"running","workerId":"wkr_01abc..."}}

event: heartbeat
data: {"timestamp":"2026-06-02T12:05:15Z"}

Daemon workers do not need to subscribe to this stream. Workers receive session work via the poll endpoint and inbox ACK mechanism. This stream is for dashboard UI clients that display live session status.

Complete lifecycle example

const sessionId = 'sess_01abc...';
const headers = {
  Authorization: `Bearer ${runtimeJwt}`,
  'Content-Type': 'application/json',
};
const base = 'https://app.rensei.ai';

// 1. Transition to running
await fetch(`${base}/api/sessions/${sessionId}/status`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ status: 'running' }),
});

// 2. Post thought activities during execution
await fetch(`${base}/api/sessions/${sessionId}/activity`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ type: 'thought', content: 'Reading the issue context...' }),
});

// 3. Refresh lock periodically
setInterval(() => {
  fetch(`${base}/api/sessions/${sessionId}/lock-refresh`, { method: 'POST', headers });
}, 60_000);

// 4. Transition to finalizing
await fetch(`${base}/api/sessions/${sessionId}/status`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ status: 'finalizing' }),
});

// 5. Post completion summary
await fetch(`${base}/api/sessions/${sessionId}/completion`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    summary: '## Changes\n\nFixed the bug. All tests pass.',
    pullRequestUrl: 'https://github.com/org/repo/pull/99',
  }),
});

// 6. Post response activity (closes the Linear AgentSession)
await fetch(`${base}/api/sessions/${sessionId}/activity`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    type: 'response',
    content: 'Done! PR #99 is ready for review.',
  }),
});

// 7. Mark completed
await fetch(`${base}/api/sessions/${sessionId}/status`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ status: 'completed' }),
});

On this page