openapi: 3.1.0 info: title: Choruz API version: 0.1.0 description: > Implementation route inventory for the local Choruz API Gateway. Routes classified under x-choruz-route-classes.humanOperatorInternal or x-choruz-route-classes.operationalInternal are documented so the console and operator tooling contract is visible; they are not ordinary third-party API integration endpoints. servers: - url: http://localhost:3000 x-choruz-route-classes: humanOperatorInternal: description: > Human-only routes gated by require_human_operator. These surfaces are intended for the local console, runtime operation, and local tooling. operations: - PATCH /v1/principals/{principalId}/workspace - POST /v1/agents/batch-disable - GET /v1/agents/{agentId}/tasks - GET /v1/agents/{agentId}/cron - POST /v1/agents/{agentId}/cron - PATCH /v1/agents/{agentId}/cron/{jobId} - DELETE /v1/agents/{agentId}/cron/{jobId} - GET /v1/runtime/bindings - POST /v1/runtime/bindings - GET /v1/runtime/bindings/{bindingId} - POST /v1/runtime/bindings/{bindingId}/rebind - GET /v1/runtime/policies/{conversationId} - PUT /v1/runtime/policies/{conversationId} - GET /v1/conversations/{conversationId}/runtime-status - PATCH /v1/conversations/{conversationId}/workspace - POST /v1/companies/{companyId}/reset-sessions - GET /v1/filesystem/list - GET /v1/filesystem/stat - GET /v1/filesystem/home - GET /v1/filesystem/read - POST /v1/filesystem/write - GET /v1/ssh/hosts - POST /v1/ssh/tunnel - GET /v1/ssh/tunnels - DELETE /v1/ssh/tunnel/{id} - POST /v1/ssh/connect-choruz operationalInternal: description: > Local service health, telemetry, console, and terminal-control surfaces. These routes support the deployed Choruz application and are not presented as stable third-party integration APIs. operations: - GET /healthz - GET /readyz - GET /metrics - GET /v1/status - GET /v1/bootstrap - GET /v1/console - POST /v1/webhooks/flush - POST /v1/telemetry - GET /v1/ws/terminals/{bindingId} - POST /v1/terminals/{bindingId}/ensure - POST /v1/terminals/{bindingId}/input paths: /v1/online/groups: get: summary: List this Online account's shared groups and invitations responses: '200': {description: 'Actor-scoped group links with message_count, latest_seq and a last_message preview (content limited to 200 characters, created_at); no encryption keys'} /v1/online/groups/invite: post: summary: Invite one Online account to an owned group description: Returns a single-use invitation containing an endpoint-only encryption key. Never log or send the invitation to telemetry. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [conversation_id] properties: conversation_id: {type: string} responses: '200': {description: Invitation and expiry} '403': {description: Only the group owner can invite} /v1/online/groups/join: post: summary: Accept an Online group invitation requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [invitation] properties: invitation: {type: string, writeOnly: true, maxLength: 4096} responses: '200': {description: Group pending owner acknowledgement} '400': {description: Invitation invalid, expired or consumed} /v1/online/groups/{link_id}: parameters: - in: path name: link_id required: true schema: {type: string} delete: summary: Leave a joined group or revoke an owned invitation responses: '204': {description: Local access disabled and cloud link revoked} /v1/online/groups/{link_id}/agents: get: summary: List this user's eligible Agents and their membership in an Online guest group responses: '200': description: Agents with id, name, status and safe error; the link must belong to the signed-in human post: summary: Request membership for an existing Agent owned by the guest device requestBody: required: true content: application/json: schema: type: object required: [agent_id] additionalProperties: false properties: agent_id: { type: string } responses: '202': description: Membership is pending until the group owner accepts the encrypted registration /v1/online/groups/{link_id}/agents/{agent_id}: delete: summary: Stop sharing this user's Agent and queue removal on the owner device responses: '204': description: Local execution stopped; remote membership removal queued durably /v1/online/groups/{link_id}/messages: parameters: - in: path name: link_id required: true schema: {type: string} get: summary: Read shared group messages after a stable sequence cursor parameters: - in: query name: after_seq schema: {type: integer, minimum: 0, default: 0} responses: '200': {description: 'Up to 100 messages in ascending sequence. Shared author context contains display-only owner_name, device_name, account_name and harness strings, never account credentials or runtime paths.'} post: summary: Queue a guest message for the owner's existing group pipeline requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [id, content] properties: id: {type: string, format: uuid} content: {type: string, minLength: 1, maxLength: 32000} responses: '202': {description: Durably queued; delivery is asynchronous} '409': {description: Group inactive or message ID already used} /v1/online/session: get: summary: Read the current human's Online account without exposing its credential description: State is signed_out, signed_in or reauth_required. Signed-in responses include account_id, device_id, display_name and a separate mailbox connection status, never the cloud session token. Network failure returns 500 rather than claiming the account is online. responses: '200': description: Signed-in account or signed-out state '429': description: Account verification throttled; the local identity is retained headers: Retry-After: {schema: {type: integer}, description: Seconds until another attempt} delete: summary: Revoke the Online session and disconnect this local identity responses: '204': description: Session revoked and local identity removed /v1/online/sign-in: post: summary: Sign the current local human into the Online account service requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [email, password] properties: email: {type: string, format: email, maxLength: 254} password: {type: string, writeOnly: true, maxLength: 128} responses: '200': description: Account connected to this local identity '400': {description: Credentials rejected} '401': {description: Local authentication required} '403': {description: Human operator required} '409': {description: Sign out before changing accounts} '429': description: Account service rate limit; retry after the advertised delay headers: Retry-After: {schema: {type: integer}, description: Seconds until another attempt} '500': {description: Account service or local storage unavailable} /v1/online/sign-up: post: summary: Register an Online account and connect the current local human requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [email, password, name] properties: email: {type: string, format: email, maxLength: 254} password: {type: string, writeOnly: true, minLength: 12, maxLength: 128} name: {type: string, minLength: 1, maxLength: 80} responses: '200': description: Account registered and connected '400': {description: Registration rejected} '401': {description: Local authentication required} '403': {description: Human operator required} '409': {description: Sign out before changing accounts} '429': description: Account service rate limit; retry after the advertised delay headers: Retry-After: {schema: {type: integer}, description: Seconds until another attempt} '500': {description: Account service or local storage unavailable} /v1/activity: get: summary: Read the signed-in human's activity records description: Requires since and until RFC3339 timestamps spanning at most 31 days. source is telemetry (default) or audit. limit is 1–500 (default 100). Optional cursor is opaque; trace_id filters a trace. Returns records and next_cursor. Scope comes only from authentication. Message content is not included. responses: '200': {description: Activity page} '400': {description: Invalid range or cursor} '401': {description: Authentication required} '403': {description: Human operator required} /v1/activity/summary: get: summary: Aggregate the signed-in human's activity description: Same source, since, until and trace_id filters as activity reads. Returns per-name counts, explicit failed outcomes and mean recorded duration; truncated is true if more than 200 groups exist. Counts are events, not unique users or inferred satisfaction. Pagination parameters are not accepted. responses: '200': {description: Activity summary} '400': {description: Invalid filters} '401': {description: Authentication required} '403': {description: Human operator required} /v1/activity/prune: post: summary: Preview or delete the signed-in human's old telemetry description: JSON body requires before (RFC3339, at least 24 hours old); apply defaults to false. Operates on at most 1000 rows per call and returns count and has_more. Explicit apply deletes only this actor's telemetry and records the deletion in the same transaction. Audit logs and conversations are never deleted. requestBody: required: true content: application/json: schema: type: object required: [before] properties: before: {type: string, format: date-time} apply: {type: boolean, default: false} responses: '200': {description: Preview or applied batch} '400': {description: Invalid cutoff} '401': {description: Authentication required} '403': {description: Human operator required} /healthz: get: operationId: healthCheck responses: "200": description: Process is alive and serving HTTP content: application/json: schema: $ref: "#/components/schemas/HostServiceStatus" /readyz: get: operationId: readinessCheck responses: "200": description: Service dependencies are ready content: application/json: schema: $ref: "#/components/schemas/HostServiceStatus" "503": description: Service is alive but not ready content: application/json: schema: $ref: "#/components/schemas/HostServiceStatus" /metrics: get: operationId: metricsSnapshot responses: "200": description: Prometheus metrics /v1/status: get: operationId: phaseStatus responses: "200": description: Product phase status /v1/auth/local/login: post: operationId: localLogin responses: "200": description: Local user session created /v1/auth/local/signup: post: operationId: localSignup responses: "201": description: Local account created /v1/me: get: operationId: getCurrentPrincipal responses: "200": description: Current authenticated principal /v1/console: get: operationId: fetchConsoleSnapshot responses: "200": description: Console snapshot content: application/json: schema: type: object additionalProperties: true properties: pinned_conversations: type: array items: $ref: "#/components/schemas/PinnedConversation" archived_conversations: type: array items: $ref: "#/components/schemas/ArchivedConversation" /v1/bootstrap: get: operationId: fetchDashboardBootstrap parameters: - in: query name: limit schema: type: integer minimum: 1 maximum: 100 default: 50 - in: query name: after schema: type: string description: Opaque cursor returned by the preceding page responses: "200": description: Bounded, activity-ordered dashboard bootstrap page content: application/json: schema: type: object required: [principal, companies, agents, conversations, sync_cursor, snapshot_at] additionalProperties: true properties: conversations: type: object required: [items, has_more] properties: items: type: array items: type: object additionalProperties: true next_cursor: oneOf: - type: string - type: "null" has_more: type: boolean sync_cursor: type: integer format: int64 minimum: 0 description: >- Durable change-feed high-water captured before the snapshot. Replay /v1/sync after this cursor; duplicate state is possible, missed concurrent mutations are not. "400": description: Invalid pagination cursor "401": description: Authentication required /v1/sync: get: operationId: listSyncChanges description: >- Non-destructive, principal-scoped dashboard gap-fill feed. Each client persists its own cursor; reading this endpoint never acknowledges or hides events from another client. parameters: - in: query name: cursor schema: type: integer format: int64 minimum: 0 default: 0 - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 responses: "200": description: Oldest-unseen-first page of durable dashboard changes content: application/json: schema: type: object additionalProperties: false required: [changes, next_cursor, head_cursor, has_more] properties: changes: type: array items: type: object additionalProperties: false required: [cursor, event_type, entity_type, entity_id, payload, created_at] properties: cursor: type: integer format: int64 event_type: type: string entity_type: type: string entity_id: type: string conversation_id: oneOf: - type: string - type: "null" payload: type: object additionalProperties: true created_at: type: string format: date-time next_cursor: type: integer format: int64 head_cursor: type: integer format: int64 has_more: type: boolean "400": description: Invalid cursor "401": description: Authentication required /v1/ws/sync: get: operationId: connectDashboardSync description: >- Upgrade to the authenticated dashboard WebSocket. Server frames replay durable changes oldest-first; clients persist progress by sending {"type":"sync_ack","cursor":N}. Delivery alone never advances the per-device acknowledgement. parameters: - in: query name: device_id required: true schema: type: string minLength: 1 maxLength: 128 - in: query name: cursor schema: type: integer format: int64 minimum: 0 default: 0 responses: "101": description: WebSocket upgrade accepted "400": description: Invalid device or cursor "401": description: Authentication required "500": description: Durable wakeup listener unavailable /v1/runtime/bindings: get: operationId: listRuntimeBindings responses: "200": description: Runtime binding list post: operationId: createRuntimeBinding responses: "201": description: Runtime binding created /v1/runtime/bindings/{bindingId}: parameters: - name: bindingId in: path required: true schema: type: string get: operationId: getRuntimeBinding responses: "200": description: Runtime binding details /v1/runtime/bindings/{bindingId}/rebind: parameters: - name: bindingId in: path required: true schema: type: string post: operationId: rebindRuntimeBinding responses: "200": description: Runtime binding rebound /v1/runtime/policies/{conversationId}: parameters: - name: conversationId in: path required: true schema: type: string get: operationId: getRuntimePolicy responses: "200": description: Runtime policy details content: application/json: schema: $ref: "#/components/schemas/RuntimePolicy" put: operationId: upsertRuntimePolicy requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpsertRuntimePolicyRequest" responses: "200": description: Runtime policy upserted content: application/json: schema: $ref: "#/components/schemas/RuntimePolicy" /v1/principals/{principalId}/disable: post: operationId: disablePrincipal parameters: - name: principalId in: path required: true schema: type: string responses: "200": description: Principal disabled /v1/principals/{principalId}/workspace: patch: operationId: migratePrincipalWorkspace parameters: - name: principalId in: path required: true schema: type: string responses: "200": description: Principal workspace migrated /v1/agents: post: operationId: createAgent responses: "201": description: Agent created /v1/agents/batch-disable: post: operationId: batchDisableAgents responses: "200": description: Agents disabled /v1/agents/{agentId}/rotate-secret: post: operationId: rotateAgentSecret parameters: - name: agentId in: path required: true schema: type: string responses: "200": description: Secret rotated /v1/agents/{agentId}/tasks: get: operationId: getAgentTasks parameters: - name: agentId in: path required: true schema: type: string responses: "200": description: Agent task list /v1/agents/{agentId}/cron: parameters: - name: agentId in: path required: true schema: type: string get: operationId: listAgentCronJobs responses: "200": description: Agent cron job list post: operationId: createAgentCronJob responses: "200": description: Agent cron job created "400": description: Invalid schedule expression, timezone, interval, or one-shot timestamp /v1/agents/{agentId}/cron/{jobId}: parameters: - name: agentId in: path required: true schema: type: string - name: jobId in: path required: true schema: type: string patch: operationId: updateAgentCronJob responses: "200": description: Agent cron job updated "400": description: Invalid schedule expression, timezone, interval, or one-shot timestamp delete: operationId: deleteAgentCronJob responses: "200": description: Agent cron job deleted /v1/conversations: get: operationId: listConversations responses: "200": description: Conversation list /v1/conversations/direct: post: operationId: createDirectConversation responses: "201": description: Direct conversation created or reused /v1/conversations/{conversationId}/pin: put: operationId: pinConversation parameters: - name: conversationId in: path required: true schema: type: string responses: "204": description: Conversation pinned for the authenticated user "403": description: Principal cannot pin this conversation delete: operationId: unpinConversation parameters: - name: conversationId in: path required: true schema: type: string responses: "204": description: Conversation unpinned for the authenticated user "403": description: Agent principals cannot unpin conversations /v1/conversations/{conversationId}/archive: put: operationId: archiveConversation parameters: - name: conversationId in: path required: true schema: type: string responses: "204": description: Conversation archived for the authenticated user "403": description: Principal cannot archive this conversation delete: operationId: unarchiveConversation parameters: - name: conversationId in: path required: true schema: type: string responses: "204": description: Conversation restored for the authenticated user "403": description: Agent principals cannot restore conversations /v1/conversations/{conversationId}/workspace: patch: operationId: migrateConversationWorkspace parameters: - name: conversationId in: path required: true schema: type: string responses: "200": description: Conversation workspace migrated /v1/conversations/{conversationId}/interactions: get: operationId: listInteractions description: Human-operator read of committed message records and their execution outcomes. Active conversation membership is required. Content is omitted unless include_content=true; raw terminal keystrokes and tool payloads are not returned. Newest sequence first, with an exclusive before_seq cursor. parameters: - name: conversationId in: path required: true schema: { type: string } - name: before_seq in: query schema: { type: integer, minimum: 0 } - name: limit in: query schema: { type: integer, minimum: 1, maximum: 100, default: 50 } - name: include_content in: query schema: { type: boolean, default: false } responses: "200": description: Records and next_before_seq. Each record identifies its event, sender role, trace, and execution attempts without duplicating the underlying message store. The read is audited. "400": description: Invalid pagination "401": description: Authentication required "403": description: Human operator with conversation membership required /v1/conversations/{conversationId}/messages: get: operationId: listMessages parameters: - name: conversationId in: path required: true schema: type: string responses: "200": description: Message list /v1/conversations/{conversationId}/message-page: get: operationId: listMessagePage description: Keyset-paginated message history. before_seq and after_seq are mutually exclusive. parameters: - name: conversationId in: path required: true schema: type: string - name: principal_id in: query required: true schema: type: string - name: before_seq in: query schema: type: integer minimum: 0 - name: after_seq in: query schema: type: integer minimum: 0 - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 responses: "200": description: Chronologically ordered page and cursor for the requested direction content: application/json: schema: type: object required: [messages, direction, has_more] additionalProperties: false properties: messages: type: array items: type: object additionalProperties: true direction: type: string enum: [latest, before, after] has_more: type: boolean next_cursor: oneOf: - type: integer - type: "null" "400": description: Invalid or ambiguous cursor "403": description: Conversation is not visible to the caller /v1/conversations/{conversationId}/tasks: parameters: - name: conversationId in: path required: true schema: type: string get: operationId: listChannelTasks responses: "200": description: Redacted channel task list for a task-enabled conversation content: application/json: schema: type: array items: $ref: "#/components/schemas/ChannelTask" "403": description: Channel tasks disabled or caller is not a conversation member post: operationId: createChannelTask description: Internal/group-agent command creation endpoint. Human empty-form create is not available in MVP. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateChannelTaskRequest" responses: "201": description: Agent-created channel task created content: application/json: schema: $ref: "#/components/schemas/ChannelTask" "200": description: Existing agent-created task returned for the same idempotency key and payload content: application/json: schema: $ref: "#/components/schemas/ChannelTask" "400": description: Invalid title, task key, idempotency key, status, or assignee "403": description: Channel tasks disabled or caller is not authorized "409": description: Task key conflict or idempotency key reused with a different payload /v1/conversations/{conversationId}/tasks/from-message: parameters: - name: conversationId in: path required: true schema: type: string post: operationId: createChannelTaskFromMessage requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateChannelTaskFromMessageRequest" responses: "201": description: Message-derived channel task created content: application/json: schema: $ref: "#/components/schemas/ChannelTask" "200": description: Existing message-derived task returned for the same source message and creator content: application/json: schema: $ref: "#/components/schemas/ChannelTask" "400": description: Invalid source message, title, or assignee "403": description: Channel tasks disabled, caller is not a member, or conversation is not eligible "409": description: Task key or idempotency conflict not covered by source-message dedupe /v1/tasks/{taskId}: parameters: - name: taskId in: path required: true schema: type: string get: operationId: getChannelTask responses: "200": description: Channel task detail with recent redacted history content: application/json: schema: $ref: "#/components/schemas/ChannelTaskDetail" "403": description: Channel tasks disabled or caller is not a conversation member "404": description: Channel task not found patch: operationId: patchChannelTask requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/PatchChannelTaskRequest" responses: "200": description: Channel task updated content: application/json: schema: $ref: "#/components/schemas/ChannelTask" "400": description: Invalid status, assignee, blocked reason, or context label "403": description: Channel tasks disabled, caller is not a member, or caller cannot supervise tasks "404": description: Channel task not found /v1/conversations/{conversationId}/runtime-status: parameters: - name: conversationId in: path required: true schema: type: string get: operationId: getConversationRuntimeStatus responses: "200": description: Derived per-agent runtime queue status for a conversation content: application/json: schema: type: array items: $ref: "#/components/schemas/ConversationRuntimeStatus" /v1/conversations/{conversationId}/view: post: operationId: viewConversation parameters: - name: conversationId in: path required: true schema: type: string responses: "200": description: Conversation marked viewed /v1/unreads: get: operationId: getUnreads responses: "200": description: Unread counts /v1/groups: post: operationId: createGroup responses: "201": description: Group created /v1/groups/{conversationId}: patch: operationId: updateGroup parameters: - name: conversationId in: path required: true schema: type: string responses: "200": description: Group metadata updated /v1/groups/{conversationId}/members: post: operationId: addGroupMembers parameters: - name: conversationId in: path required: true schema: type: string responses: "200": description: Members added /v1/groups/{conversationId}/members/{principalId}: delete: operationId: removeGroupMember parameters: - name: conversationId in: path required: true schema: type: string - name: principalId in: path required: true schema: type: string responses: "200": description: Member removed /v1/messages: post: operationId: sendMessage responses: "201": description: Message accepted /v1/messages/search: get: operationId: searchMessages parameters: - name: principal_id in: query required: true schema: type: string - name: q in: query required: true schema: type: string minLength: 1 - name: conversation_id in: query schema: type: string - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 - name: before_created_at in: query description: Older-result cursor timestamp; provide together with before_message_id. schema: type: string format: date-time - name: before_message_id in: query description: Cursor message ID; provide together with before_created_at from the last result. schema: type: string responses: "200": description: Authorized message search results ordered by created_at and message_id descending; the cursor excludes its boundary result. "400": description: Empty search query, malformed cursor, or incomplete cursor pair /v1/attachments: post: operationId: uploadAttachment responses: "201": description: Attachment stored /v1/attachments/{attachmentId}: get: operationId: downloadAttachment parameters: - name: attachmentId in: path required: true schema: type: string responses: "200": description: Attachment payload delete: operationId: deleteAttachment parameters: - name: attachmentId in: path required: true schema: type: string responses: "200": description: Attachment deleted /v1/principals/{principalId}/events: get: operationId: listEvents parameters: - name: principalId in: path required: true schema: type: string responses: "200": description: Event backlog /v1/principals/{principalId}/events/ack: post: operationId: ackEvents parameters: - name: principalId in: path required: true schema: type: string responses: "200": description: Events acknowledged /v1/principals/{principalId}/event-webhook: post: operationId: setEventWebhook parameters: - name: principalId in: path required: true schema: type: string responses: "200": description: Event webhook configured /v1/webhooks/flush: post: operationId: flushPendingWebhooks responses: "200": description: Pending event webhooks flushed /v1/runtime/bindings/{bindingId}/session: get: operationId: readStructuredSession parameters: - {name: bindingId, in: path, required: true, schema: {type: string}} - {name: after, in: query, schema: {type: integer, minimum: 0, default: 0}} responses: "200": description: Authorized direct conversation state on the binding's execution device post: operationId: openStructuredSession parameters: - {name: bindingId, in: path, required: true, schema: {type: string}} responses: "200": description: Reattached or started the binding's structured Harness process "409": description: The binding is owned by an interactive terminal or another active turn /v1/runtime/bindings/{bindingId}/session/commands: post: operationId: commandStructuredSession parameters: - {name: bindingId, in: path, required: true, schema: {type: string}} requestBody: required: true content: application/json: schema: type: object required: [action, instance] properties: action: {type: string, enum: [send, respond, interrupt, close]} instance: {type: string, description: Process instance returned by the current session snapshot} text: {type: string, maxLength: 131072} submission_id: {type: string, maxLength: 128} request_id: {} allow: {type: boolean} answers: {type: object, additionalProperties: {type: array, items: {type: string}}} responses: "200": description: Accepted command and current structured conversation state "409": description: Active turn, stale approval or conflicting submission /v1/ws/terminals/{bindingId}: get: operationId: streamTerminalWebSocket parameters: - name: bindingId in: path required: true schema: type: string responses: "101": description: Terminal WebSocket upgraded /v1/ws/harness-logins/{companyId}/{accountId}/{loginId}: parameters: - { name: companyId, in: path, required: true, schema: { type: string } } - { name: accountId, in: path, required: true, schema: { type: string } } - { name: loginId, in: path, required: true, schema: { type: string } } get: operationId: streamClaudeLoginTerminal description: Login creator's private official Claude Code PTY. Raw input and output are not persisted as activity or traces. Closing the stream stops the terminal. responses: "101": description: Official Claude Code terminal connected "404": description: No active Claude login owned by this user /v1/drivers/models: get: operationId: inspectLocalDriverModels parameters: - { name: driver_type, in: query, required: true, schema: { type: string } } responses: "200": description: Installed CLI model catalog on this device /v1/ws/runtime-hosts/link: get: operationId: openRuntimeHostLink summary: A paired device's persistent host link (WebSocket; the first frame is a hello carrying the host token) description: Terminal bytes use terminal_output frames. terminal_resync ends a lagging attachment without terminating its process; reattach for a current screen snapshot. terminal_exit denotes a stopped terminal. Revoked host tokens cannot reconnect. responses: "101": description: Switching Protocols /v1/telemetry: post: operationId: ingestTelemetry description: Authenticated activity batches, at most 100 events. Each event carries eventId, schemaVersion (1), traceId, spanId, sessionId, name, ts (RFC3339), optional durationMs and sanitized data. Workspace and actor are derived from authentication. The whole batch commits before acknowledgement; replayed event IDs do not create duplicate rows. requestBody: required: true content: application/json: schema: type: object required: [events] properties: events: type: array minItems: 1 maxItems: 100 items: type: object additionalProperties: false required: [eventId, schemaVersion, traceId, spanId, sessionId, name, ts] properties: eventId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9_.:-]+$'} schemaVersion: {type: integer, enum: [1]} traceId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9_.:-]+$'} spanId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9_.:-]+$'} sessionId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9_.:-]+$'} name: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9_.:-]+$'} ts: {type: string, format: date-time} durationMs: {type: integer, minimum: 0} data: {type: object, description: Sanitized properties up to 16384 serialized bytes} responses: "204": description: Entire batch durably committed or already present "400": description: Invalid batch; no events committed "422": description: Event fields do not match the wire schema; no events committed "401": description: Authentication required "500": description: Persistence failed; retry the same event IDs /v1/terminals/{bindingId}/ensure: post: operationId: ensureTerminal description: Starts the binding on its runtime device. An explicit binding binary_path names an executable on that device; otherwise that device resolves its configured Harness executable. parameters: - name: bindingId in: path required: true schema: type: string responses: "200": description: Terminal session ensured /v1/terminals/{bindingId}/input: post: operationId: sendTerminalInput parameters: - name: bindingId in: path required: true schema: type: string responses: "200": description: Terminal input accepted /v1/companies: get: operationId: listCompanies responses: "200": description: Company list post: operationId: createCompany responses: "201": description: Company created /v1/companies/{companyId}: parameters: - name: companyId in: path required: true schema: type: string get: operationId: getCompany responses: "200": description: Company details patch: operationId: updateCompany responses: "200": description: Company updated delete: operationId: deleteCompany responses: "200": description: Company deleted /v1/companies/{companyId}/archive: parameters: - name: companyId in: path required: true schema: type: string post: operationId: archiveCompany responses: "200": description: Company archived /v1/companies/{companyId}/unarchive: parameters: - name: companyId in: path required: true schema: type: string post: operationId: unarchiveCompany responses: "200": description: Company unarchived /v1/companies/{companyId}/reset-sessions: parameters: - name: companyId in: path required: true schema: type: string post: operationId: resetCompanySessions responses: "200": description: Company agent sessions reset /v1/companies/{companyId}/members: parameters: - name: companyId in: path required: true schema: type: string get: operationId: listCompanyMembers responses: "200": description: Company member list post: operationId: addCompanyMember responses: "200": description: Company member added /v1/companies/{companyId}/members/{memberId}: parameters: - name: companyId in: path required: true schema: type: string - name: memberId in: path required: true schema: type: string delete: operationId: removeCompanyMember responses: "200": description: Company member removed /v1/companies/{companyId}/harness-accounts/{accountId}: parameters: - name: companyId in: path required: true schema: type: string - name: accountId in: path required: true schema: type: string delete: operationId: removeHarnessAccount summary: Fence new work and stop owned account processes before completing removal responses: "200": description: Account processes stopped and removal completed "202": description: Removal persisted and waiting for device cleanup or active command completion "403": description: Human company access required "404": description: Account not found in this company /v1/companies/{companyId}/harness-accounts/{accountId}/probe: parameters: - name: companyId in: path required: true schema: type: string - name: accountId in: path required: true schema: type: string post: operationId: probeHarnessAccount summary: Read the account's identity, models and exact quota on the device that holds its login and store the snapshot responses: "204": description: Snapshot stored "409": description: The probe failed; the reason is recorded on the account /v1/companies/{companyId}/harness-accounts/{accountId}/logins: parameters: - name: companyId in: path required: true schema: type: string - name: accountId in: path required: true schema: type: string post: operationId: startHarnessAccountLogin description: >- Resume the account's unexpired in-progress login, or start the Harness's own sign-in when none exists. Claude waits in `authorizing` for the creator to open its official CLI terminal. Codex uses browser sign-in, queued for a remote connector or driven in-process on the gateway's device. responses: "200": description: Existing in-progress Harness account login view "201": description: Harness account login view "404": description: Harness account not found in this company /v1/companies/{companyId}/harness-accounts/{accountId}/logins/{loginId}: parameters: - name: companyId in: path required: true schema: type: string - name: accountId in: path required: true schema: type: string - name: loginId in: path required: true schema: type: string get: operationId: getHarnessAccountLogin responses: "200": description: Harness account login view; an open login past its expiry reads as `expired` "404": description: Harness account login not found /v1/companies/{companyId}/harness-accounts/{accountId}/logins/{loginId}/callback: parameters: - name: companyId in: path required: true schema: type: string - name: accountId in: path required: true schema: type: string - name: loginId in: path required: true schema: type: string post: operationId: submitHarnessAccountLoginCallback description: Hand the pasted authorization code or callback URL to the running sign-in. responses: "204": description: Authorization code accepted "409": description: This login is no longer waiting for an authorization code /v1/companies/{companyId}/harness-accounts/{accountId}/logins/{loginId}/cancel: parameters: - name: companyId in: path required: true schema: type: string - name: accountId in: path required: true schema: type: string - name: loginId in: path required: true schema: type: string post: operationId: cancelHarnessAccountLogin description: Close an open sign-in so the account accepts a new one at once; the in-process driver stops at its next callback poll. responses: "204": description: Login cancelled "409": description: This login is not open /v1/companies/{companyId}/harness-accounts/{accountId}/logins/{loginId}/complete: parameters: - { name: companyId, in: path, required: true, schema: { type: string } } - { name: accountId, in: path, required: true, schema: { type: string } } - { name: loginId, in: path, required: true, schema: { type: string } } post: operationId: completeClaudeTerminalLogin description: Verify the selected Claude profile through the official CLI's identity probe, independently of model and quota discovery. responses: "204": description: Account verified "400": description: Claude Code has not signed in "404": description: No active Claude login owned by this user /v1/audit-logs: get: operationId: listAuditLogs responses: "200": description: Audit log list /v1/export/conversations/{conversationId}: get: operationId: exportConversation parameters: - name: conversationId in: path required: true schema: type: string responses: "200": description: Exported conversation /v1/filesystem/list: get: operationId: filesystemList responses: "200": description: Directory listing /v1/filesystem/stat: get: operationId: filesystemStat responses: "200": description: File metadata /v1/filesystem/home: get: operationId: filesystemHome responses: "200": description: Server home directory /v1/filesystem/read: get: operationId: filesystemRead responses: "200": description: File contents /v1/filesystem/write: post: operationId: filesystemWrite description: >- Save an existing editable file only when its current content matches original_content. The comparison detects edits before saving; it is not an atomic compare-and-swap against arbitrary external writers. requestBody: required: true content: application/json: schema: type: object required: [path, content, original_content] properties: path: type: string content: type: string original_content: type: string responses: "200": description: File written "409": description: File changed; disk and draft remain unchanged content: application/json: schema: type: object required: [error, current_content] properties: error: type: string current_content: type: string /v1/ssh/hosts: get: operationId: listSshHosts responses: "200": description: SSH host list /v1/ssh/tunnel: post: operationId: createSshTunnel responses: "200": description: SSH tunnel created /v1/ssh/tunnels: get: operationId: listSshTunnels responses: "200": description: SSH tunnel list /v1/ssh/tunnel/{id}: delete: operationId: deleteSshTunnel parameters: - name: id in: path required: true schema: type: string responses: "200": description: SSH tunnel deleted /v1/ssh/connect-choruz: post: operationId: connectChoruzSshTunnel responses: "200": description: Choruz SSH tunnel connected /v2/ingest: post: operationId: ingestMessage responses: "202": description: Pipeline message accepted /v1/auth/local/bootstrap: get: operationId: bootstrapLocalOperator responses: "200": description: Local operator bootstrap /v1/companies/{companyId}/runtime-host-pairings: parameters: - name: companyId in: path required: true schema: type: string post: operationId: createRuntimeHostPairing responses: "201": description: Runtime host pairing code /v1/companies/{companyId}/runtime-hosts: parameters: - name: companyId in: path required: true schema: type: string get: operationId: listRuntimeHosts responses: "200": description: Runtime host list /v1/conversations/{conversationId}/hide: parameters: - name: conversationId in: path required: true schema: type: string put: operationId: hideAgentSession responses: "200": description: Agent session hidden delete: operationId: restoreHiddenAgentSession responses: "200": description: Agent session hidden /v1/conversations/{conversationId}/messages/{messageId}: parameters: - name: conversationId in: path required: true schema: type: string - name: messageId in: path required: true schema: type: string get: operationId: getMessage responses: "200": description: Message /v1/conversations/{conversationId}/threads/{threadRootId}: parameters: - name: conversationId in: path required: true schema: type: string - name: threadRootId in: path required: true schema: type: string get: operationId: getThread responses: "200": description: Thread /v1/conversations/{conversationId}/threads/{threadRootId}/view: parameters: - name: conversationId in: path required: true schema: type: string - name: threadRootId in: path required: true schema: type: string post: operationId: viewThread responses: "200": description: Thread marked viewed /v1/remote-control/bridge-config: get: operationId: getRemoteControlBridgeConfig responses: "200": description: Remote control bridge configuration /v1/remote-control/devices: get: operationId: listRemoteControlDevices responses: "200": description: Remote control device list /v1/remote-control/devices/{deviceId}: parameters: - name: deviceId in: path required: true schema: type: string delete: operationId: revokeRemoteControlDevice responses: "200": description: Remote control device revoked /v1/remote-control/devices/{deviceId}/seen: parameters: - name: deviceId in: path required: true schema: type: string put: operationId: touchRemoteControlDevice responses: "200": description: Remote control device seen /v1/remote-control/pairings: post: operationId: createRemoteControlPairing responses: "201": description: Remote control pairing credential /v1/remote-control/pairings/redeem: post: operationId: redeemRemoteControlPairing responses: "200": description: Remote control device paired /v1/remote-control/settings: get: operationId: getRemoteControlSettings responses: "200": description: Remote control settings /v1/runtime-host-pairings/redeem: post: operationId: redeemRuntimeHostPairing responses: "200": description: Runtime host paired /v1/runtime-host-onboarding: post: operationId: onboardRuntimeHostThroughRemoteControl responses: "200": description: Remote computer registered as a persistent Company runtime host /v1/runtime-hosts/{hostId}: parameters: - name: hostId in: path required: true schema: type: string put: operationId: renameRuntimeHost responses: "200": description: Runtime host delete: operationId: revokeRuntimeHost description: Revokes device authorization even when offline. Bound Agents remain on that device but disabled; unfinished commands are fenced and never retried on the controller. Moving an Agent requires a separate explicit assignment. responses: "204": description: Device revoked and execution disabled; history retained /v1/runtime-hosts/{hostId}/commands/claim: parameters: - name: hostId in: path required: true schema: type: string post: operationId: claimRuntimeHostCommand responses: "200": description: Claimed command or null /v1/runtime-hosts/{hostId}/commands/{commandId}/complete: parameters: - name: hostId in: path required: true schema: type: string - name: commandId in: path required: true schema: type: string post: operationId: completeRuntimeHostCommand requestBody: required: true content: application/json: schema: type: object required: [attempt_id, succeeded] properties: attempt_id: {type: string} succeeded: {type: boolean} contents: {type: array, maxItems: 100, items: {type: string, maxLength: 100000}} execution_metadata: {type: object, description: Optional finite-program evidence. Requires one matching answer, completed evaluated-model evidence, source decision_program, native_calls_avoided 1 and zero tool calls.} responses: "204": description: Command completed /v1/runtime-hosts/{hostId}/commands/{commandId}/heartbeat: parameters: - name: hostId in: path required: true schema: type: string - name: commandId in: path required: true schema: type: string post: operationId: heartbeatRuntimeHostCommand responses: "200": description: Command lease extended /v1/runtime-hosts/{hostId}/bindings/{bindingId}/outbox: parameters: - name: hostId in: path required: true schema: type: string - name: bindingId in: path required: true schema: type: string post: operationId: shipBindingOutbox summary: The connector delivers the outbox commands an agent on its device wrote; they land in the binding's controller-side mirror for this host security: - RuntimeHostToken: [] requestBody: required: true content: application/json: schema: type: object required: [commands] properties: commands: type: array maxItems: 200 description: Command files in helper order; one request carries at most 16 MiB of encoded file content items: type: object required: [name, command] properties: name: type: string description: The Maildir file name the helper assigned (`cmd--.json`) command: type: object description: The command JSON as the agent wrote it files: type: array description: Bytes of a `share_file` target, relative to the workspace, at most 8 MiB each items: type: object required: [path, content] properties: path: type: string content: type: string format: byte responses: "204": description: Commands stored for the outbox watcher "400": description: Too many commands, a file over the cap, a path outside the workspace, or content that is not base64 "401": description: Missing or revoked host token "403": description: The binding is not placed on this host /v1/runtime-hosts/{hostId}/commands/{commandId}/attachments/{attachmentId}: parameters: - name: hostId in: path required: true schema: type: string - name: commandId in: path required: true schema: type: string - name: attachmentId in: path required: true schema: type: string get: operationId: downloadCommandAttachment summary: The bytes of a file attached to a turn the host has claimed, staged into the workspace inbox before the turn runs security: - RuntimeHostToken: [] responses: "200": description: Attachment bytes with their content type "401": description: Missing or revoked host token "403": description: The command is not placed on this host, does not name the attachment, or the agent may not read it "404": description: Unknown command or attachment /v1/runtime-hosts/{hostId}/harness-account-logins/claim: parameters: - name: hostId in: path required: true schema: type: string post: operationId: claimHarnessAccountLogin responses: "200": description: Claimed login or null /v1/runtime-hosts/{hostId}/harness-account-logins/{loginId}/callback/claim: parameters: - name: hostId in: path required: true schema: type: string - name: loginId in: path required: true schema: type: string post: operationId: claimHarnessAccountLoginCallback responses: "200": description: Claimed authorization code or null /v1/runtime-hosts/{hostId}/harness-account-logins/{loginId}/complete: parameters: - name: hostId in: path required: true schema: type: string - name: loginId in: path required: true schema: type: string post: operationId: completeHarnessAccountLogin responses: "200": description: Login verified /v1/runtime-hosts/{hostId}/harness-account-logins/{loginId}/fail: parameters: - name: hostId in: path required: true schema: type: string - name: loginId in: path required: true schema: type: string post: operationId: failHarnessAccountLogin responses: "200": description: Login failed /v1/runtime-hosts/{hostId}/harness-account-logins/{loginId}/publish: parameters: - name: hostId in: path required: true schema: type: string - name: loginId in: path required: true schema: type: string post: operationId: publishHarnessAccountLogin responses: "200": description: Authorization link published /v1/runtime-hosts/{hostId}/harness-accounts: parameters: - name: hostId in: path required: true schema: type: string post: operationId: registerHarnessAccount responses: "200": description: Harness account registered /v1/runtime-hosts/{hostId}/harness-accounts/{accountId}/verify: parameters: - name: hostId in: path required: true schema: type: string - name: accountId in: path required: true schema: type: string post: operationId: verifyHarnessAccount responses: "200": description: Harness account verified /v1/runtime-hosts/{hostId}/heartbeat: parameters: - name: hostId in: path required: true schema: type: string post: operationId: heartbeatRuntimeHost responses: "200": description: Runtime host heartbeat recorded /v1/runtime-hosts/{hostId}/operations: parameters: - name: hostId in: path required: true schema: type: string post: operationId: executeRuntimeHostOperation description: Runs a device-owned operation. drivers.inspect without driver_type reports installed harnesses; with driver_type it reads that device's default-profile model catalog. responses: "200": description: Result returned by the selected runtime host /v1/runtime/bindings/{bindingId}/host: parameters: - name: bindingId in: path required: true schema: type: string put: operationId: assignBindingHost description: Explicitly changes the execution device; it does not replay commands cancelled by revocation. A selected Harness account must belong to the destination device. Use Create Agent with that device's workspace and account when moving an account-bound Agent; existing conversation history remains available. responses: "204": description: Binding host assigned /v1/runtime/bindings/{bindingId}/experience/community/{eventId}/retry: parameters: - name: bindingId in: path required: true schema: {type: string} - name: eventId in: path required: true schema: {type: string} post: operationId: retryBehaviorPreparation summary: Retry owner-scoped blocked preparation before any public dispatch responses: '200': {description: Preparation queued; public projection and privacy review run again if contribution remains enabled.} '409': {description: Record is not blocked before dispatch; uncertain contributions cannot be blindly retried.} '403': {description: Caller cannot manage the binding.} /v1/runtime/bindings/{bindingId}/experience/community: parameters: - in: path name: bindingId required: true schema: {type: string} get: summary: Read owner-scoped behavior cards, evidence counts and community status description: Local records may contain private contextual prose. Public records come only from accepted dataset revisions. Counts describe distinct reported occurrences, not model failure rates. Observed execution model is nullable and separate from configured model. responses: '200': {description: Settings, local records, accepted community records, synchronization and contribution status. No source trace or publisher credential.} '403': {description: Human owner required} put: summary: Set independent community search, trial and contribution consent description: Requires an owned learning policy. All switches default off. Trials require search. Contribution publishes only independently reviewed redacted records through dataset pull requests; disabling it cancels queued publication. No network request occurs in this handler. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [search, automatic_trial, contribute] properties: search: {type: boolean} automatic_trial: {type: boolean} contribute: {type: boolean} responses: '200': {description: Saved consent settings} '400': {description: Invalid consent combination} '403': {description: Human owner required} '404': {description: Learning policy not configured} /v1/runtime/bindings/{bindingId}/experience/decisions: parameters: - in: path name: bindingId required: true schema: {type: string} patch: operationId: selectDecisionProgram summary: Select a validated program or clear the selection requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [revision_id] properties: revision_id: {type: [string, "null"]} responses: '200': {description: Selected program pointer updated.} '403': {description: Policy not owned by the caller.} '409': {description: Program is unvalidated or incompatible with current settings.} put: operationId: configureBackgroundDecisions summary: Set opt-in decision capabilities for an owned learning policy description: Non-null settings authorize transmission of this binding's bounded evidence to TypeSafe. assist_turns sends later task inputs for advice before native execution; complete_turns separately permits an evaluated applicable finite answer to skip native inference. Both have a five-second provider budget and native fallback. Null disables decisions. Already-dispatched requests cannot be recalled; results returned after consent or selection changes are discarded. A builder must be an accessible Codex binding in the same workspace. Changing settings fences in-flight learning. Read saved settings and results through the experience endpoint. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [settings] properties: settings: type: [object, "null"] additionalProperties: false required: [model, minimum_confidence, classify, supervise, builder_binding_id] properties: model: {type: string, minLength: 1, maxLength: 128} minimum_confidence: {type: number, minimum: 0, maximum: 1} classify: {type: boolean} supervise: {type: boolean} assist_turns: {type: boolean, default: false, description: Include an opted-in selected program's bounded decision in subsequent native Agent turns. The Agent remains the executor.} complete_turns: {type: boolean, default: false, description: Allow an evaluated applicable finite answer to complete a turn without native model inference. Abstention or provider failure falls back to the native Agent. No tool actions are authorized.} builder_binding_id: {type: [string, "null"]} responses: '200': {description: Settings saved. No provider call runs in this handler.} '400': {description: Invalid settings or unsupported builder.} '403': {description: Binding or learning policy is not owned by this caller.} /v1/runtime/bindings/{bindingId}/experience/decisions/execute: parameters: - in: path name: bindingId required: true schema: {type: string} post: operationId: executeSelectedDecisionProgram summary: Execute a selected validated program without generating a new one description: Requires an enabled owned learning policy and current transmission consent. Returns an explicit abstention outside scope. A changed resolved provider model invalidates this execution. No browser or shell effects occur. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [input] properties: input: {type: string, minLength: 1, maxLength: 16000} responses: '200': {description: Program output or abstention, with decision evidence.} '403': {description: Policy or binding not owned by the caller.} '409': {description: No currently usable program or provider model changed.} /v1/runtime/bindings/{bindingId}/browser-automation: parameters: - {in: path, name: bindingId, required: true, schema: {type: string}} get: operationId: getBrowserAutomation summary: Read standing permission, learned workflows and recent run receipts responses: '200': {description: Scoped automation settings and history} '403': {description: Binding access denied} put: operationId: configureBrowserAutomation summary: Enable or stop automatic browser work once for a binding description: Only the human learning owner may configure standing permission. Saving pins the execution account, cancels current runs and invalidates pending admissions. Null disables automation. Permission covers task and browser evidence sent to the decision provider and native reasoning Agent. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [settings] properties: settings: type: [object, "null"] additionalProperties: false required: [browser, allowed_urls, scope] properties: browser: {type: string, minLength: 1, maxLength: 128} scope: {type: string, minLength: 1, maxLength: 2000} allowed_urls: {type: array, minItems: 1, maxItems: 16, items: {type: string, maxLength: 4000}} responses: '200': {description: Permission saved and cancellation acknowledgement returned} '403': {description: Human ownership required} '409': {description: Enable learning and configure a builder first} /v1/browser-workflows: get: operationId: listAgentBrowserWorkflows summary: Discover learned browser workflows under this Agent's standing permission responses: '200': {description: Draft or validated workflows scoped to the execution binding} '403': {description: Agent authentication required} /v1/runtime/bindings/{bindingId}/browser-workflows/{runId}: parameters: - {in: path, name: bindingId, required: true, schema: {type: string}} - {in: path, name: runId, required: true, schema: {type: string, maxLength: 128}} post: operationId: executeBrowserWorkflow summary: Run a learned workflow for a current task under standing permission description: The server resolves the stored workflow, browser, model and execution account. Scope and exact page URLs are checked before dispatch. The first actual task validates a workflow; historical tasks are never replayed for validation. Failed or uncertain revisions are suspended. Duplicate ids return the original receipt. Completion durably schedules one continuation, including after restart. Inputs remain transient. Visible-text checks are evidence, not proof of business correctness. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [revision_id, task, url, values, expected_text] properties: conversation_id: {type: [string, "null"], description: Originating accessible conversation; defaults to the binding conversation.} revision_id: {type: string} task: {type: string, minLength: 1, maxLength: 16000} url: {type: string, maxLength: 4000} values: {type: object, maxProperties: 16, additionalProperties: {type: string, maxLength: 4000}} text_requests: {type: object, maxProperties: 16, additionalProperties: {type: string, maxLength: 4000}} expected_text: {type: array, minItems: 1, maxItems: 16, items: {type: string, minLength: 1, maxLength: 1000}} responses: '200': {description: Durable run receipt} '403': {description: Binding or standing permission denied} '409': {description: Workflow suspended, out of scope, running or conflicting id} get: operationId: getBrowserWorkflow summary: Read the original receipt without repeating an action responses: '200': {description: Running, finished, cancelled or outcome_unconfirmed} '403': {description: Binding access denied} delete: operationId: cancelBrowserWorkflow summary: Stop an admitted run without recalling dispatched actions responses: '200': {description: Cancellation requested and device acknowledgement} '403': {description: Binding access denied} /v1/runtime/bindings/{bindingId}/decisions: parameters: - in: path name: bindingId required: true schema: {type: string} post: operationId: runStructuredDecision summary: Run a bounded structured decision on the selected Agent's device description: Human terminal access and explicit transmission consent are required. The selected device owns TYPESAFE_API_KEY. No credentials are accepted or returned. Classification and supervision are advisory; browser results select observed targets but do not execute actions or establish completion. Program output is nullable for abstention. No automatic retry occurs. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [transmit_to_typesafe, request] properties: transmit_to_typesafe: {type: boolean, enum: [true]} request: type: object additionalProperties: false required: [model, minimum_confidence, job] properties: model: {type: string, minLength: 1, maxLength: 128} minimum_confidence: {type: number, minimum: 0, maximum: 1} job: oneOf: - type: object additionalProperties: false required: [kind, state] properties: kind: {type: string, enum: [classify, supervise]} state: {$ref: '#/components/schemas/DecisionState'} - type: object additionalProperties: false required: [kind, program, state] properties: kind: {type: string, enum: [program]} program: {$ref: '#/components/schemas/DecisionProgram'} state: {$ref: '#/components/schemas/DecisionState'} - type: object additionalProperties: false required: [kind, goal, observation] properties: kind: {type: string, enum: [browser]} goal: {type: string, minLength: 1, maxLength: 16000} observation: {$ref: '#/components/schemas/DecisionObservation'} responses: '200': {description: Validated decision or explicit program abstention with model and token usage.} '400': {description: Invalid request or device provider configuration.} '403': {description: Missing consent or terminal access.} /v1/runtime/bindings/{bindingId}/experience/prepare: post: summary: Prepare learned context for an external native CLI turn description: Requires the human learning owner and binding access. Reads the current enabled revision and executes its reviewed collaborators on the binding device. Returns a prompt, not a submitted turn or evidence of success. Disabled learning returns the original prompt. Configuration or binding changes during preparation reject the result. Does not retry model calls or rewrite project files. parameters: - in: path name: bindingId required: true schema: {type: string} requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [input] properties: input: {type: string, minLength: 1, maxLength: 262144} responses: '200': description: Prepared input; the consumer must submit it to its native CLI content: application/json: schema: type: object properties: prompt: {type: string} revision_id: {type: string, nullable: true} '403': {description: Caller does not own the policy or cannot access the binding} '409': {description: Selection changed or the selected device is disconnected} /v1/runtime/bindings/{bindingId}/experience: parameters: - name: bindingId in: path required: true schema: type: string get: operationId: getExperienceLearning description: Returns the human owner's learning settings and recent revisions for an authorized Agent. Revision validation.dataset contains immutable corpus version, category coverage, added/updated/withdrawn counts, reviewed training variant counts and grouping/conflict diagnostics. Trace-case classification carries task_type, capability, structure, outcome and related objective references. task_performance contains the current executor context and training objective sample/success counts and empirical bands from up to 100 completed matching baseline runs; it is null without an explicit model. Held-out scores, changed tasks and inconclusive results are excluded. Enabled trace learning periodically rereads retained evidence on a daily cycle. Source traces are not returned by this endpoint. responses: "200": description: Settings and revision history patch: operationId: selectExperienceRevision description: Selects a previously reviewed revision for future tasks, or clears the active revision. Fences any in-flight learning result. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [revision_id] properties: revision_id: type: [string, "null"] responses: "200": description: Updated learning settings put: operationId: configureExperienceLearning description: Enables or disables asynchronous experience learning with a separately selected analysis Agent in the same workspace. Changes fence pending analysis; foreground execution never waits for learning. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [enabled, analyst_binding_id] properties: enabled: type: boolean analyst_binding_id: type: string target_model: type: string description: Explicit task model. Changing it requires an idle Agent and preserves native session history. analyst_model: type: string description: Explicit analysis model. Selected models commit together after both Agents are idle. reuse_for_new_tasks: type: boolean default: false description: Reuse this owned policy for matching new tasks. Enabling reuse clears previous public-contribution consent. optimization_settings: type: [object, 'null'] additionalProperties: false required: [suite, config, auto_apply] description: Opt-in background optimization using a supplied suite or reviewed trace cases. Trace mode accepts an empty suite and freezes available cases at run creation. Auto-apply requires validation improvement, held-out non-regression and final content review. Null uses content-review learning without measured search. properties: trace_cases: type: boolean default: false description: Build the suite asynchronously from evidenced work objectives. Wait for independent objectives covering all splits; never invent answers to fill the suite. suite: type: object additionalProperties: false required: [name, cases] description: May be empty in trace_cases mode; otherwise requires the fixed evaluation suite contract. properties: name: type: string cases: type: array items: $ref: '#/paths/~1v1~1runtime~1bindings~1{bindingId}~1experience~1evaluations/post/requestBody/content/application~1json/schema/properties/suite/properties/cases/items' config: $ref: '#/paths/~1v1~1runtime~1bindings~1{bindingId}~1experience~1evaluations/post/requestBody/content/application~1json/schema/properties/optimization' auto_apply: type: boolean responses: "200": description: Saved settings and revision history /v1/runtime/bindings/{bindingId}/experience/evaluations: parameters: - name: bindingId in: path required: true schema: type: string get: operationId: listExperienceEvaluations description: Returns the owner's twenty most recent comparison summaries, reserved call counts and application status. Supply id for one full run with frozen snapshots, ancestry and per-case results, including judge rationale and replay observations. Only an opted-in automatic run can apply its reviewed winner. Results do not establish performance outside the supplied task suite. parameters: - name: id in: query schema: type: string responses: "200": description: Evaluation history post: operationId: createExperienceEvaluation description: Queues an asynchronous comparison of a reviewed candidate against the current guidance on the target account. Freezes the supplied suite and both revisions. Model calls run in fresh scratch conversations, never the live session. Explicit environments enable isolated container actions. Calls reserve the executor step bound plus internal team members and an independent judge when requested. An inconclusive judge result stops the run without a numeric score. An interrupted call is not retried. Settings or execution-context changes cancel the run. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [revision_id, suite] properties: revision_id: type: string optimization: type: object additionalProperties: false required: [max_metric_calls, max_proposals, minibatch_size, seed, merge] properties: max_metric_calls: type: integer minimum: 1 maximum: 512 max_proposals: type: integer minimum: 1 maximum: 32 minibatch_size: type: integer minimum: 1 seed: type: integer minimum: 0 merge: type: boolean cache_evaluations: type: boolean default: false description: Reuse previous candidate/case observations; otherwise measure each comparison afresh. evolve_team: type: boolean default: false description: Allow bounded internal team changes after a documented recurring problem; analysis standards remain fixed. max_agents: type: integer minimum: 1 maximum: 4 default: 4 description: Maximum total agents per task, including the final executor; all use the target's existing device and account. suite: type: object additionalProperties: false required: [name, cases] properties: name: type: string maxLength: 120 cases: type: array minItems: 3 maxItems: 64 items: type: object additionalProperties: false required: [id, split, input, check] properties: source: type: [object, 'null'] description: Frozen objective provenance for automatically extracted cases, including episode_ref, evidence, input, check and reason. environment: type: [object, 'null'] additionalProperties: false required: [image, files, verification, max_steps] description: Explicit reproducible starting state. Runs in a network-disabled disposable container, never the live workspace. The digest-pinned image must already exist on the selected device. properties: image: type: string pattern: '^sha256:[a-f0-9]{64}$' files: type: object additionalProperties: type: string description: Bounded UTF-8 files at relative paths, with no symlinks or parent traversal. verification: type: array minItems: 1 maxItems: 8 items: type: string description: Fixed shell checks applied after solving; every check must exit zero. Commands are withheld from the solver. max_steps: type: integer minimum: 1 maximum: 8 id: type: string maxLength: 80 split: type: string enum: [train, validation, test] input: type: string maxLength: 16000 check: oneOf: - type: object additionalProperties: false required: [type, expected] properties: type: const: exact expected: type: string - type: object additionalProperties: false required: [type, expected] properties: type: const: json expected: {} - type: object additionalProperties: false required: [type, expected, rubric] properties: type: const: judge expected: type: string minLength: 1 maxLength: 8000 description: Reference result, not an exact-match answer. rubric: type: string minLength: 1 maxLength: 4000 description: Frozen task-specific acceptance criteria interpreted by the platform judge skill. Insufficient evidence is not a failure score. responses: "202": description: Immutable comparison queued "409": description: An evaluation is already pending or the candidate is not eligible /v1/companies/{companyId}/computer-use: parameters: - name: companyId in: path required: true schema: type: string post: operationId: manageComputerUse description: Checks device browser and desktop tools. An optional tool and enabled boolean persist automatic skill provisioning and start missing tool installation on the selected runtime_host_id, or the local device when omitted. Installation continues independently of the browser request. Only human company members may use this operation. Disabling does not uninstall tools or revoke operating-system permissions. requestBody: required: true content: application/json: schema: type: object additionalProperties: false properties: runtime_host_id: type: string tool: type: string enum: [browser, desktop] enabled: type: boolean responses: "200": description: Device-local tool installation and health state /v1/workspace-sessions/import: post: operationId: importWorkspaceSessions description: Imports verified sessions from the selected device and company account stores. Each selection may carry harness_account_id for an isolated profile; the server rescans that owned profile before binding it. The owner of a deleted company may reclaim its idle session after the previous terminal stops; the previous Agent and binding are disabled and their history retained. responses: "200": description: Workspace sessions imported "404": description: The pi or opencode plugin is disabled on the controller "409": description: Session belongs to another active company or owner, has an in-flight turn, or its previous terminal has not stopped /v1/workspace-sessions/scan: post: operationId: scanWorkspaceSessions description: Scans the device default stores and the selected company's isolated account stores on that device. Results carry harness_account_id for isolated profiles, preserving profile identity through import and resume. responses: "200": description: Workspace session scan result "404": description: The pi or opencode plugin is disabled on the controller, or the workspace does not exist components: securitySchemes: RuntimeHostToken: type: apiKey in: header name: x-choruz-host-token description: The token a runtime host received when its pairing code was redeemed schemas: DecisionState: description: Bounded task data, not instructions or authorization. Total encoded provider request is limited to 256 KiB. oneOf: - {type: string} - {type: object, additionalProperties: true} - {type: array, items: {}} DecisionInstructions: description: A nonempty string (up to 8000 UTF-8 bytes), object or array (up to 8000 JSON-encoded bytes). Question IDs are not inference instructions. Questions in one request cannot read each other's answers. oneOf: - {type: string, minLength: 1, maxLength: 8000} - {type: object, minProperties: 1, additionalProperties: true} - {type: array, minItems: 1, items: {}} DecisionRubric: description: A string, nonempty object or nonempty array, bounded to 4000 bytes (JSON-encoded for containers). Null leaves a Choice or Noul option undescribed; Score levels must be nonempty and non-null. oneOf: - {type: string, nullable: true, maxLength: 4000} - {type: object, minProperties: 1, additionalProperties: true} - {type: array, minItems: 1, items: {}} DecisionQuestion: oneOf: - type: object additionalProperties: false required: [type, instructions, criteria] properties: type: {type: string, enum: [choice]} instructions: {$ref: '#/components/schemas/DecisionInstructions'} criteria: type: object minProperties: 2 maxProperties: 255 additionalProperties: {$ref: '#/components/schemas/DecisionRubric'} - type: object additionalProperties: false required: [type, instructions, criteria] properties: type: {type: string, enum: [score]} instructions: {$ref: '#/components/schemas/DecisionInstructions'} criteria: type: array minItems: 2 maxItems: 10 items: description: Nonempty, non-null level; up to 4000 bytes. oneOf: - {type: string, minLength: 1, maxLength: 4000} - {type: object, minProperties: 1, additionalProperties: true} - {type: array, minItems: 1, items: {}} - type: object additionalProperties: false required: [type, instructions] properties: type: {type: string, enum: [noul]} instructions: {$ref: '#/components/schemas/DecisionInstructions'} criteria: type: object additionalProperties: false properties: 'true': {$ref: '#/components/schemas/DecisionRubric'} 'false': {$ref: '#/components/schemas/DecisionRubric'} DecisionProgram: type: object additionalProperties: false required: [name, applicability, questions, result_question, outputs, minimum_confidence] description: Finite-output program. The result question must be Choice with an abstain option. Every other option must have exactly one output. The reserved _applicable head cannot be supplied. String bounds are enforced in UTF-8 bytes. properties: name: {type: string, minLength: 1, maxLength: 128} applicability: {type: string, minLength: 1, maxLength: 4000} questions: type: object minProperties: 1 maxProperties: 63 additionalProperties: {$ref: '#/components/schemas/DecisionQuestion'} result_question: {type: string, minLength: 1, maxLength: 128} outputs: type: object additionalProperties: {type: string, minLength: 1, maxLength: 16000} minimum_confidence: {type: number, minimum: 0, maximum: 1} DecisionObservation: type: object additionalProperties: false required: [id, url, elements] properties: id: {type: string, minLength: 1, maxLength: 128} url: {type: string, maxLength: 4000} elements: type: array maxItems: 254 items: type: object additionalProperties: false required: [id, description, operations] properties: id: {type: string, minLength: 1, maxLength: 128, description: Unique within this observation; cannot be none.} description: {type: string, minLength: 1, maxLength: 2000} operations: type: array maxItems: 3 items: {type: string, enum: [click, type_text, select]} HostServiceStatus: type: object additionalProperties: false required: - status - service - protocol_version properties: status: type: string enum: [ok, ready, not_ready] service: type: string protocol_version: type: integer format: int32 minimum: 1 database: type: boolean ConversationRuntimeStatus: type: object required: - conversation_id - agent_principal_id - agent_name - status - queued_count - active_command - last_error properties: conversation_id: type: string agent_principal_id: type: string agent_name: type: string status: type: string enum: - idle - queued - busy queued_count: type: integer minimum: 0 active_command: oneOf: - $ref: "#/components/schemas/RuntimeStatusCommand" - type: "null" last_error: type: - string - "null" RuntimeStatusCommand: type: object required: - command_id - message_id - turn_id - status - created_at - updated_at - lease_age_seconds - attempt_count - last_error properties: command_id: type: string message_id: type: string turn_id: type: string status: type: string enum: - pending - leased - started - heartbeating - succeeded - committed - retry_scheduled - dead_letter created_at: type: string format: date-time updated_at: type: string format: date-time lease_age_seconds: type: - integer - "null" minimum: 0 attempt_count: type: integer minimum: 0 last_error: type: - string - "null" RuntimePolicy: type: object required: - conversation_id - auto_mode - max_auto_turns - max_workflow_turns - require_human_after_n_turns - allow_agent_to_agent - allow_file_write - default_reviewer_agent_id - default_coordinator_agent_id - untagged_human_mode properties: conversation_id: type: string auto_mode: type: string enum: - disabled - mentioned_only - metadata_only max_auto_turns: type: integer max_workflow_turns: type: integer require_human_after_n_turns: type: integer allow_agent_to_agent: type: boolean allow_file_write: type: boolean default_reviewer_agent_id: type: - string - "null" default_coordinator_agent_id: type: - string - "null" untagged_human_mode: type: string enum: - mentioned_only - coordinator_only - all_agents UpsertRuntimePolicyRequest: type: object additionalProperties: false properties: auto_mode: type: string enum: - disabled - mentioned_only - metadata_only max_auto_turns: type: integer max_workflow_turns: type: integer require_human_after_n_turns: type: integer allow_agent_to_agent: type: boolean allow_file_write: type: boolean default_reviewer_agent_id: type: string default_coordinator_agent_id: type: string untagged_human_mode: type: string enum: - mentioned_only - coordinator_only - all_agents PinnedConversation: type: object required: - conversation_id - pinned_at properties: conversation_id: type: string pinned_at: type: string format: date-time ArchivedConversation: type: object required: - conversation_id - archived_at properties: conversation_id: type: string archived_at: type: string format: date-time ChannelTaskStatus: type: string enum: - todo - in_progress - blocked - in_review - done ChannelTaskSourceKind: type: string enum: - agent - message PrincipalType: type: string enum: - human - agent ChannelTask: type: object additionalProperties: false required: - task_id - conversation_id - task_key - title - status - source_kind - version - created_at - updated_at properties: task_id: type: string conversation_id: type: string task_key: type: string title: type: string status: $ref: "#/components/schemas/ChannelTaskStatus" assignee_principal_id: type: string assignee_type: $ref: "#/components/schemas/PrincipalType" assignee_name: type: string blocked_reason: type: string context_label: type: string source_kind: $ref: "#/components/schemas/ChannelTaskSourceKind" source_message_id: type: string created_by: type: string created_by_type: $ref: "#/components/schemas/PrincipalType" updated_by: type: string updated_by_type: $ref: "#/components/schemas/PrincipalType" version: type: integer format: int64 minimum: 1 created_at: type: string format: date-time updated_at: type: string format: date-time ChannelTaskVisibleValues: type: object additionalProperties: false properties: status: $ref: "#/components/schemas/ChannelTaskStatus" assignee_principal_id: type: string blocked_reason: oneOf: - type: string - type: "null" context_label: oneOf: - type: string - type: "null" source_kind: $ref: "#/components/schemas/ChannelTaskSourceKind" source_message_id: type: string ChannelTaskEvent: type: object additionalProperties: false required: - event_id - task_id - kind - created_at properties: event_id: type: string task_id: type: string kind: type: string actor_principal_id: type: string actor_type: $ref: "#/components/schemas/PrincipalType" created_at: type: string format: date-time resulting_version: type: integer format: int64 previous: $ref: "#/components/schemas/ChannelTaskVisibleValues" new: $ref: "#/components/schemas/ChannelTaskVisibleValues" workflow_kind: type: string status_effect: $ref: "#/components/schemas/ChannelTaskStatus" reason_code: type: string ChannelTaskDetail: type: object additionalProperties: false required: - task - events properties: task: $ref: "#/components/schemas/ChannelTask" events: type: array description: Recent redacted task history, ordered oldest-to-newest within the returned window. maxItems: 50 items: $ref: "#/components/schemas/ChannelTaskEvent" CreateChannelTaskRequest: type: object additionalProperties: false required: - title - idempotency_key properties: task_key: type: string description: >- Optional. When omitted the server allocates a sequential `TASK-{N}` key scoped to the conversation. Callers may still pass an explicit key for backwards compatibility. title: type: string assignee_principal_id: type: string status: $ref: "#/components/schemas/ChannelTaskStatus" context_label: type: string idempotency_key: type: string CreateChannelTaskFromMessageRequest: type: object additionalProperties: false required: - message_id - title - assignee_principal_id properties: message_id: type: string title: type: string assignee_principal_id: type: string context_label: oneOf: - type: string - type: "null" idempotency_key: type: string PatchChannelTaskRequest: type: object additionalProperties: false minProperties: 1 properties: status: $ref: "#/components/schemas/ChannelTaskStatus" assignee_principal_id: type: string blocked_reason: oneOf: - type: string - type: "null" context_label: oneOf: - type: string - type: "null"